Set up

Packages

# Wrangling
library(tidyverse)
library(mgsub)
library(naniar)

# Statistics/Numerical processing
library(brms)

# PCA
library(FactoMineR) 
library(factoextra) 
library(GPArotation)
library(paran)

# Plotting
library(ggplot2)
library(ghibli)

# Optional settings
options(dplyr.summarise.inform=F) # Stop dplyr from printing summarise error (that isn't an error)
select <- dplyr::select # Ensure that select() command is the dplyr command (clashes with MASS, which is imported/required by paran)

Functions

# Standard error function
std.error <- function(x, na.rm = T) {
  sqrt(var(x, na.rm = na.rm)/length(x[complete.cases(x)]))
}

# ggplot theme
gg_theme <- function() {
  theme_bw() +
  theme(plot.title=element_text(size=25),
        plot.subtitle=element_text(size=15, face="italic"),
        axis.title=element_text(size=20),
        axis.text=element_text(size=15),
        strip.background =element_rect(fill="white"),
        strip.text = element_text(size=15))+
  theme(legend.title = element_text(size=15, face="bold"),
        legend.text=element_text(size=15))
}

Perception Data

Pre-Process Data

Read & Clean Results

Read in the data from downloaded CSV files from Firebase. Also remove data from subjects who did not complete the study (“returned” on Prolific) or have been identified to be outliers, not following instructions, not fulfilling my participant requirements, etc. Outliers are dentified in a later section below (Outlier Check), while participant requirements are checked in the data from the questionnaire.

# List results files per subject
filelist <- list.files(path="./data/axb_1b/perception/", pattern=".csv",full.names=TRUE) 

# Check Number of files
paste("Total number of perception results files in directory:",length(filelist)) 
[1] "Total number of perception results files in directory: 101"
paste("Expected number of questionnaire files:",length(filelist)-2-2) # -2 (repeats) -2 (returns) 
[1] "Expected number of questionnaire files: 97"

In this case, when reading in the data by participant file, the experiment start time is extracted from the structure ./data/axb_XX/perception/subjnumber_starttime.csv where I first remove the “.csv” with gsub, split the string into three based on "_", then select the third item from the first output object ([[1]][3]).

# Read and Concatenate results
## (1) Just read and concat
# condata.read <- do.call(rbind, lapply(filelist, read.csv))

## (2) Read, concat and extract part of filename
condata.read <- do.call(rbind, lapply(filelist, function(x) cbind(read.csv(x),
                                      starttime=strsplit(gsub(".csv","",x), "_")[[1]][3])))
## TODO: Update with subject numbers as necessary
condata.read <- condata.read %>% 
  
  # Fix data with undefined subject
  mutate(participantId = replace_na(participantId, '5d1e2045a37a4d001a1fc2cb')) %>%
  
  # Remove data from dropped subjects
  subset(participantId != '5a68d1c1c0d83600010821e0') %>%   # did not finish -- RETURNED
  subset(participantId != '5fcd0ee406bd7ab94ecc424c') %>%   # spokane + 50% too fast -- RETURNED
  subset(participantId != '5e67f0321e4f0a0a657c1d08') %>%   # not michigan - florida to 22
  subset(participantId != '5f9f96c7ea7f965f92f5d488') %>%   # not michigan - colorado to 18
  subset(participantId != '5ffa01eb7dfb43387a868196') %>%   # not michigan - ny to 28
  subset(participantId != '5fee8cf38ac18214460c59ed') %>%   # not michigan - pa to 15
  subset(participantId != '5699d25625d9e9000db0c7cc') %>%   # 168 + 85 guy... did twice(?) and skipped all
  subset(participantId != '601f04fb235cb14020c7a96f') %>%   # did twice, once with condA then condC, so can't use even full data
  subset(participantId != '5fe0e7892738afa6a21cfd7c') %>%   # did not finish study/questionnaire -- RETURNED
# subset(participantId != '5fc511c32b9bc60bc189b893') %>%   # alien guy --- KEPT in the end

  # Remove levels of dropped subjects
  droplevels()
# Trim unnecesssary columns and rows
condata <- subset(condata.read,,-c(url, internal_node_id, view_history, stimulus, success, key_press, trial_index, 
                                    trial_type, wordStim))

# Keep only tonetest and test rows
# Create subject number column by order of participation (first sorting subjects by starttime, then adding subjnum column)
# Recover vowel data from sentNum column (== Vowel data was accidently left out of stimuli data)
# Manipulate data types
# Reorder columns
condata <- condata %>%
  subset(trial_role == "test" | trial_role == "tonetest") %>%
  
  #arrange(desc(starttime)) %>%
  #mutate(subjNum = as.factor(rep(1:nSubj, each=(nrow(.)/nSubj)))) %>%
  
  mutate(vowel = case_when(between(sentNum, 11, 20) ~ "AU",
                           between(sentNum, 21, 30) ~ "AI")) %>%
  mutate(guiseCombination = case_when(conditionId == "condA" | conditionId == "condB" ~ "match",
                                        conditionId == "condC" | conditionId == "condD" ~ "mismatch",
                                        conditionId == "condE" | conditionId == "condF" ~ "baseline")) %>%
  mutate(speakerOrder = case_when(conditionId == "condA" | conditionId == "condC" | conditionId == "condE" ~ "S3-S9",
                                  conditionId == "condB" | conditionId == "condD" | conditionId == "condF" ~ "S9-S3")) %>%
  
  mutate(rt=as.numeric(rt)) %>%
  mutate_if(is.character, as.factor) %>%
  mutate(time_elapsed_sec = time_elapsed/1000, rt_sec = rt/1000) %>%
  
  select(participantId, conditionId, trial_role, time_elapsed, time_elapsed_sec, rt, rt_sec, everything())

# Check data
condata
#summary(condata)

Check Results

Check Participants

# Check number of data points per subject
# Correct number of data points is 174/180/186 = (168 + 6/12/18) 
(nData.bysubj <- condata %>%
  group_by(conditionId, participantId, starttime) %>%
  count())
# Check number of data points per condition (goal: 40 per condition)
(nSubj.bycond <- nData.bysubj %>%
  group_by(conditionId) %>%
  count())

Troubleshoot participant issues manually and/or by filtering duplicate subject files or those with too few responses.

# Manually scroll to check data as needed
View(nData.bysubj)
# Troubleshoot duplicated participants
## Calculate number of subjects vs data files

paste("Total number of screened perception data files:",nrow(nData.bysubj)) # number of data files
[1] "Total number of screened perception data files: 90"
paste("Total number of unique participant numbers:",length(unique(condata.read$participantId))) # unique subj nums
[1] "Total number of unique participant numbers: 90"
## Identify dups
nData.bysubj$dups = duplicated(nData.bysubj$participantId)
nData.bysubj %>% filter(dups==TRUE)

# Troubleshoot half data
nData.bysubj %>% filter(n<174)
NA

Check Tone Test

# select "Tonetest" data
# Check which participants got 4/6 or below on the headphone/attention check
condata.tones <- condata %>%
  subset(trial_role == "tonetest") %>%
  mutate(correct_response=tolower(correct_response)) %>%
  group_by(participantId, conditionId) %>%
  slice_max(order_by = time_elapsed, n = 6) # get last 6 tone trials, by largest time_elapsed

condata.tones <- condata.tones %>%
  group_by(participantId, conditionId) %>%
  summarise(tonesCorrect = sum(correct_response=="true")) %>%
  ungroup()
condata.tones
# Troubleshoot half data
condata.tones %>% filter(tonesCorrect<5)

Check AXB Trials

# Load stim.durations.final dataframe
load(file="./data/stim_durations.rData")

# Select "Test" data + remove columns for Tonetest data
# Merge tonesCorrect column in for later decisions (e.g. if remove people with low score in tone test)

condata.test <- condata %>%
  subset(trial_role == "test") %>%
  subset(.,,-c(button_pressed, correct_answer, correct_response)) %>%
  droplevels() %>%
  
  merge(., condata.tones, all.x=TRUE) %>%
  merge(., stim.durations.final, all.x=TRUE) %>%
  
  select(participantId, conditionId, trial_role, time_elapsed, time_elapsed_sec, rt, rt_sec, raised_response, everything())
condata.test
# Check data for obvious issues
summary(condata.test)
                  participantId   conditionId  trial_role    time_elapsed     time_elapsed_sec        rt            rt_sec      
 56d9f54b610fc0000bb76e8d:  168   condA:5376   test:15120   Min.   :  73779   Min.   :  73.78   Min.   :  128   Min.   : 0.128  
 56e72067c89073000be77fdf:  168   condC:4536                1st Qu.: 499867   1st Qu.: 499.87   1st Qu.: 3171   1st Qu.: 3.171  
 59c5689f46f72100019066a7:  168   condE:5208                Median : 751400   Median : 751.40   Median : 3520   Median : 3.520  
 5a412f0b99311d0001df431e:  168                             Mean   : 864770   Mean   : 864.77   Mean   : 3743   Mean   : 3.743  
 5b14791ebd9c31000156e8ab:  168                             3rd Qu.:1005121   3rd Qu.:1005.12   3rd Qu.: 3965   3rd Qu.: 3.965  
 5bef64b373e44f0001092811:  168                             Max.   :4908639   Max.   :4908.64   Max.   :44512   Max.   :44.512  
 (Other)                 :14112                                                                                                 
 raised_response vowel     speaker       sentNum      order           step   speakerIdentity speakerGuise guiseName    raised_answer
 false:7296      AI:7560   S3:15120   Min.   :18.00   axb:7560   Min.   :2   MI:15120        BL:5208      S3-BL:5208   Min.   :0.0  
 true :7824      AU:7560              1st Qu.:19.00   bxa:7560   1st Qu.:3                   CN:4536      S3-CN:4536   1st Qu.:0.0  
                                      Median :20.50              Median :5                   MI:5376      S3-MI:5376   Median :0.5  
                                      Mean   :21.67              Mean   :5                                             Mean   :0.5  
                                      3rd Qu.:22.00              3rd Qu.:7                                             3rd Qu.:1.0  
                                      Max.   :30.00              Max.   :8                                             Max.   :1.0  
                                                                                                                                    
  key_response            starttime     guiseCombination speakerOrder   tonesCorrect      dur_sec          dur_ms     quart_dur_sec  
 Min.   :0.0000   1612319713122:  168   baseline:5208    S3-S9:15120   Min.   :0.000   Min.   :1.799   Min.   :1799   Min.   :1.349  
 1st Qu.:0.0000   1612320743251:  168   match   :5376                  1st Qu.:5.000   1st Qu.:2.176   1st Qu.:2176   1st Qu.:1.632  
 Median :1.0000   1612320747221:  168   mismatch:4536                  Median :6.000   Median :2.406   Median :2406   Median :1.804  
 Mean   :0.5964   1612322477195:  168                                  Mean   :5.522   Mean   :2.415   Mean   :2415   Mean   :1.811  
 3rd Qu.:1.0000   1612323119149:  168                                  3rd Qu.:6.000   3rd Qu.:2.682   3rd Qu.:2682   3rd Qu.:2.011  
 Max.   :1.0000   1612323133046:  168                                  Max.   :6.000   Max.   :3.022   Max.   :3022   Max.   :2.266  
                  (Other)      :14112                                                                                                
  quart_dur_ms 
 Min.   :1349  
 1st Qu.:1632  
 Median :1804  
 Mean   :1811  
 3rd Qu.:2011  
 Max.   :2266  
               
# Check original data points
(datapoints.og <- nrow(condata.test))
[1] 15120

Check AXB Outliers

# What are the descriptive stats on total experiment time and reaction times?
condata.test.bysubj <- condata.test %>%
  group_by(participantId, conditionId) %>%
  summarize(time_elapsed_min = max(time_elapsed_sec/60), mean_rt_sec = mean(rt_sec), min_rt_sec = min(rt_sec),  max_rt_sec = max(rt_sec), sd_rt_sec = sd(rt_sec)) %>%
  ungroup()
head(condata.test.bysubj)

# Summary of total experiment times
# Check for especially short or long times
condata.test.overall <- condata.test.bysubj %>%
  summarize(median_time = median(time_elapsed_min), mean_time= mean(time_elapsed_min), min_time = min(time_elapsed_min), max_time = max(time_elapsed_min))
condata.test.overall
# RT Outlier check
# Calculate response times that are at least 3 SDs away from the mean
condata.test.timesum <- condata.test %>%
  summarize(meanTime = mean(rt), sdTime = sd(rt), minTime = min(rt), maxTime = max(rt), medianTime = median(rt), iqrTime = IQR(rt), meanStimTime = mean(dur_ms)) %>%
  mutate(sd3 = sdTime*3, iqr3 = iqrTime*3, stimTime10 = meanStimTime+10000)
condata.test.timesum
# Add columns of outlier criteria 
condata.test.check <- condata.test %>%
  mutate(rt.outlier.lower = rt < quart_dur_ms, rt.outlier.upper = rt > (dur_ms + 10000))

# Check list of outliers that were removed
condata.test.outliers <- condata.test.check %>%
  subset(rt.outlier.lower == TRUE | rt.outlier.upper == TRUE)
summary(condata.test.outliers)
                  participantId conditionId trial_role  time_elapsed     time_elapsed_sec       rt              rt_sec        raised_response
 5e42f74f5b772a18434cabf7:14    condA:13    test:74    Min.   : 183267   Min.   : 183.3   Min.   :  128.0   Min.   : 0.1280   false:39       
 5fc511c32b9bc60bc189b893: 7    condC:26               1st Qu.: 568489   1st Qu.: 568.5   1st Qu.:  868.5   1st Qu.: 0.8685   true :35       
 5fc769df1f4e27017a638e8e: 5    condE:35               Median : 845758   Median : 845.8   Median : 7299.0   Median : 7.2990                  
 5fdf9d13a6a9ed7d8efd0b69: 4                           Mean   : 880377   Mean   : 880.4   Mean   :10097.6   Mean   :10.0976                  
 60053227125e504142df91e9: 4                           3rd Qu.:1052409   3rd Qu.:1052.4   3rd Qu.:16339.5   3rd Qu.:16.3395                  
 5dd27daedba63428af7caf09: 3                           Max.   :2055174   Max.   :2055.2   Max.   :44512.0   Max.   :44.5120                  
 (Other)                 :37                                                                                                                 
 vowel   speaker    sentNum      order         step       speakerIdentity speakerGuise guiseName  raised_answer    key_response   
 AI:36   S3:74   Min.   :18.00   axb:39   Min.   :2.000   MI:74           BL:35        S3-BL:35   Min.   :0.000   Min.   :0.0000  
 AU:38           1st Qu.:19.00   bxa:35   1st Qu.:4.000                   CN:26        S3-CN:26   1st Qu.:0.000   1st Qu.:0.0000  
                 Median :20.00            Median :5.000                   MI:13        S3-MI:13   Median :0.000   Median :1.0000  
                 Mean   :21.89            Mean   :4.919                                           Mean   :0.473   Mean   :0.5135  
                 3rd Qu.:22.00            3rd Qu.:6.750                                           3rd Qu.:1.000   3rd Qu.:1.0000  
                 Max.   :30.00            Max.   :8.000                                           Max.   :1.000   Max.   :1.0000  
                                                                                                                                  
         starttime  guiseCombination speakerOrder  tonesCorrect      dur_sec          dur_ms     quart_dur_sec    quart_dur_ms 
 1612323462402:14   baseline:35      S3-S9:74     Min.   :0.000   Min.   :1.799   Min.   :1799   Min.   :1.349   Min.   :1349  
 1612320747221: 7   match   :13                   1st Qu.:5.000   1st Qu.:2.176   1st Qu.:2176   1st Qu.:1.632   1st Qu.:1632  
 1612406509940: 5   mismatch:26                   Median :6.000   Median :2.540   Median :2540   Median :1.905   Median :1905  
 1612323119149: 4                                 Mean   :5.581   Mean   :2.432   Mean   :2432   Mean   :1.824   Mean   :1824  
 1612366388975: 4                                 3rd Qu.:6.000   3rd Qu.:2.682   3rd Qu.:2682   3rd Qu.:2.011   3rd Qu.:2011  
 1612372452274: 3                                 Max.   :6.000   Max.   :3.022   Max.   :3022   Max.   :2.266   Max.   :2266  
 (Other)      :37                                                                                                              
 rt.outlier.lower rt.outlier.upper
 Mode :logical    Mode :logical   
 FALSE:37         FALSE:37        
 TRUE :37         TRUE :37        
                                  
                                  
                                  
                                  
# Summarize number of outliers attributed to each participant
condata.outliers.bysubj <- condata.test.outliers %>% #filter(conditionId=="condC") %>%
    group_by(participantId, conditionId) %>%
    count(sort=TRUE)
condata.outliers.bysubj
View(condata.test.outliers)
# A
4/168 #0.02380952 subj 60053227125e504142df91e9
[1] 0.02380952
# C
# 85/168 #0.5059524 subj 5699d25625d9e9000db0c7cc **BUT ALSO IN COND E???
14/168 #0.08333333 subj 5e42f74f5b772a18434cabf7 --- all were longer; can keep if necessary
[1] 0.08333333
4/168 #0.02380952 subj 5a68d1c1c0d83600010821e0
[1] 0.02380952
# E
# 168/168 #0.5059524 subj 5699d25625d9e9000db0c7cc **BUT ALSO IN COND C???
# 85/168 #0.5059524 5fcd0ee406bd7ab94ecc424c --- the spokane 18-year-old
7/168 #0.04166667 subj 5fc511c32b9bc60bc189b893 --- all were shorter...; also the alien guy
[1] 0.04166667
5/168 #0.0297619 5fc769df1f4e27017a638e8e
[1] 0.0297619
4/168 #0.02380952 subj 5fdf9d13a6a9ed7d8efd0b69
[1] 0.02380952

Go back to top and remove outlier participants, if necessary. Then rerun everything up to this point.

Finalize Results

# Remove outliers
condata.test.final <- setdiff(condata.test.check, condata.test.outliers)

# Group data by StimType (i.e. Speaker-SpeakerGuise-Vowel-SentNum)
condata.test.final <- condata.test.final %>%
  unite(token, speaker, sentNum, remove=FALSE) %>%
  
  mutate(word = case_when(token == "S3_21" ~ "bright",
                   token == "S3_22" ~ "device",
                   token == "S3_30" ~ "twice",
                   token == "S9_23" ~ "goodnight",
                   token == "S9_25" ~ "invite",
                   token == "S9_29" ~ "sight",
                   token == "S3_18" ~ "slouch",
                   token == "S3_19" ~ "without",
                   token == "S3_20" ~ "workout",
                   token == "S9_11" ~ "checkout",
                   token == "S9_18" ~ "sprouts",
                   token == "S9_20" ~ "workout")) %>%
  mutate(item = paste0(speaker,"_",word)) %>%
  mutate(respRS = case_when(raised_response == "true" ~ 1,
                            raised_response == "false" ~ 0)) %>%
  
  unite(stimType_byword, speaker, speakerGuise, vowel, word, remove=FALSE) %>%
  unite(stimType_byvowel, speaker, speakerGuise, vowel, remove=FALSE) %>%
  
  mutate(sentNum = as.factor(sentNum)) %>%
  mutate(step = (step-5)) %>%
  
  select(participantId, guiseCombination, step, vowel, speakerGuise, speaker, word, speakerOrder, respRS, stimType_byword, stimType_byvowel, everything())

# Summary
summary(condata.test.final)
                  participantId   guiseCombination      step            vowel     speakerGuise speaker        word           speakerOrder 
 56d9f54b610fc0000bb76e8d:  168   baseline:5173    Min.   :-3.0000000   AI:7524   BL:5173      S3:15046   Length:15046       S3-S9:15046  
 59c5689f46f72100019066a7:  168   match   :5363    1st Qu.:-2.0000000   AU:7522   CN:4510                 Class :character                
 5a412f0b99311d0001df431e:  168   mismatch:4510    Median : 0.0000000             MI:5363                 Mode  :character                
 5b14791ebd9c31000156e8ab:  168                    Mean   : 0.0003988                                                                     
 5bf49e46cade8c0001e8387f:  168                    3rd Qu.: 2.0000000                                                                     
 5c1c181289f03500017281f9:  168                    Max.   : 3.0000000                                                                     
 (Other)                 :14038                                                                                                           
     respRS       stimType_byword    stimType_byvowel   conditionId  trial_role    time_elapsed     time_elapsed_sec        rt       
 Min.   :0.0000   Length:15046       Length:15046       condA:5363   test:15046   Min.   :  73779   Min.   :  73.78   Min.   : 1704  
 1st Qu.:0.0000   Class :character   Class :character   condC:4510                1st Qu.: 499486   1st Qu.: 499.49   1st Qu.: 3171  
 Median :1.0000   Mode  :character   Mode  :character   condE:5173                Median : 751208   Median : 751.21   Median : 3520  
 Mean   :0.5177                                                                   Mean   : 864693   Mean   : 864.69   Mean   : 3711  
 3rd Qu.:1.0000                                                                   3rd Qu.:1004309   3rd Qu.:1004.31   3rd Qu.: 3960  
 Max.   :1.0000                                                                   Max.   :4908639   Max.   :4908.64   Max.   :12600  
                                                                                                                                     
     rt_sec       raised_response    token           sentNum   order      speakerIdentity guiseName    raised_answer     key_response   
 Min.   : 1.704   false:7257      Length:15046       18:2509   axb:7521   MI:15046        S3-BL:5173   Min.   :0.0000   Min.   :0.0000  
 1st Qu.: 3.171   true :7789      Class :character   19:2507   bxa:7525                   S3-CN:4510   1st Qu.:0.0000   1st Qu.:0.0000  
 Median : 3.520                   Mode  :character   20:2506                              S3-MI:5363   Median :1.0000   Median :1.0000  
 Mean   : 3.711                                      21:2511                                           Mean   :0.5001   Mean   :0.5968  
 3rd Qu.: 3.960                                      22:2507                                           3rd Qu.:1.0000   3rd Qu.:1.0000  
 Max.   :12.600                                      30:2506                                           Max.   :1.0000   Max.   :1.0000  
                                                                                                                                        
         starttime      tonesCorrect      dur_sec          dur_ms     quart_dur_sec    quart_dur_ms  rt.outlier.lower rt.outlier.upper
 1612320743251:  168   Min.   :0.000   Min.   :1.799   Min.   :1799   Min.   :1.349   Min.   :1349   Mode :logical    Mode :logical   
 1612322477195:  168   1st Qu.:5.000   1st Qu.:2.176   1st Qu.:2176   1st Qu.:1.632   1st Qu.:1632   FALSE:15046      FALSE:15046     
 1612323133046:  168   Median :6.000   Median :2.272   Median :2272   Median :1.704   Median :1704                                    
 1612323295513:  168   Mean   :5.522   Mean   :2.415   Mean   :2415   Mean   :1.811   Mean   :1811                                    
 1612323671492:  168   3rd Qu.:6.000   3rd Qu.:2.682   3rd Qu.:2682   3rd Qu.:2.011   3rd Qu.:2011                                    
 1612366088661:  168   Max.   :6.000   Max.   :3.022   Max.   :3022   Max.   :2.266   Max.   :2266                                    
 (Other)      :14038                                                                                                                  
     item          
 Length:15046      
 Class :character  
 Mode  :character  
                   
                   
                   
                   
# Print data
condata.test.final

# Write to file
write.csv(condata.test.final, 'data/axb_1b_exp_data.csv', row.names=F)
# Final kept data points
(datapoints.final <- nrow(condata.test.final))
[1] 15046
# Final removed data points
datapoints.og-datapoints.final
[1] 74
# Calculate percentage of data removed
(datapoints.og-datapoints.final)/datapoints.og # = 0.008852259 = 0.8% of the data were removed due to responses that were too quick (less than 3/4 of the time into the audio file, before the third token would have played) or too slow (over 10 sec after the end of the audio file, an arbitrarily chosen value that should be enough time if a participant were responding as quickly as possible)
[1] 0.00489418

Summarize Data

Plots

PropRS by Guise

# Get subj means per condition
subj.means <- condata.test.final %>% #filter(participantId!='5e42f74f5b772a18434cabf7') %>%
  group_by(participantId, step, vowel, speakerGuise) %>%
  summarise(mean.Prop = mean(respRS))

# Get group means and se per condition (by averaging speaker means)
condition.means <- subj.means %>%
  group_by(step, vowel, speakerGuise) %>%
  summarise(grandM.Prop = mean(mean.Prop), se = std.error(mean.Prop))

# Plot lineplot with error bars on step points
byGuise_prop_plot <- condition.means %>%
  ggplot(aes(x = step, y = grandM.Prop)) +
  geom_point(stat="identity", aes(colour = factor(speakerGuise)), cex=5) +
  geom_line(aes(colour=factor(speakerGuise), linetype=factor(speakerGuise)), lwd=1) +
  geom_errorbar(width = .25, aes(ymin = grandM.Prop-se, ymax = grandM.Prop+se, 
                                 colour = factor(speakerGuise))) +
  facet_grid(~vowel) +
  labs(y = "Proportion 'raised' response", x = "Continuum Step (UR to RS)", color="Guise",
       linetype = "Guise", title="Raising Perception: By Guise") +
  coord_cartesian(ylim=c(0, 1)) +
  scale_x_continuous(breaks = -3:3) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,4,3)]) +
  gg_theme()
byGuise_prop_plot

RT by Guise

# Get subj means per condition
subj.means <- condata.test.final %>% filter(speaker=='S3') %>% #filter(participantId!='5e42f74f5b772a18434cabf7') %>%
  group_by(participantId, step, vowel, speakerGuise) %>%
  summarise(mean.rt = mean(log(rt)))

# Get group means and se per condition (by averaging speaker means)
condition.means <- subj.means %>%
  group_by(step, vowel, speakerGuise) %>%
  summarise(grandM.rt = mean(mean.rt), se = std.error(mean.rt))

# Plot lineplot with error bars on step points
byGuise_rt_plot <- condition.means %>%
  ggplot(aes(x = step, y = grandM.rt)) +
  geom_point(stat="identity", aes(colour = factor(speakerGuise)), cex=5) +
  geom_line(aes(colour=factor(speakerGuise), linetype=factor(speakerGuise)), lwd=1) +
  geom_errorbar(width = .25, aes(ymin = grandM.rt-se, ymax = grandM.rt+se, colour = factor(speakerGuise))) +
  facet_grid(~vowel) +
  labs(y = "Log Response Time", x = "Continuum Step (UR to RS)", color="Guise",
       linetype = "Guise", title="Reaction Time: By Guise") +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,4,3)])+
  gg_theme()
byGuise_rt_plot

PropRS by Word

# Get subj means per condition
subj.means <- condata.test.final %>% filter(speaker=="S3") %>%
  group_by(participantId, step, vowel, speakerGuise, speaker, word) %>%
  summarise(mean.Prop = mean(respRS))

# Get group means and se per condition (by averaging speaker means)
condition.means <- subj.means %>%
  group_by(step, vowel, speakerGuise, speaker, word) %>%
  summarise(grandM.Prop = mean(mean.Prop), se = std.error(mean.Prop))

# AI
byWord_prop_plot <- condition.means %>% filter(vowel=="AI") %>%
  ggplot(aes(x = step, y = grandM.Prop)) +
  geom_point(stat="identity", aes(colour = factor(speakerGuise)), cex=5, alpha=0.75) +
  geom_line(aes(colour=factor(speakerGuise), linetype=factor(word)), lwd=1) +
  geom_errorbar(width = .25, aes(ymin = grandM.Prop-se, ymax = grandM.Prop+se, colour = factor(speakerGuise))) +
  facet_grid(speaker~word) +
  labs(y = "Proportion 'raised' response", x = "Continuum Step (UR to RS)", color="Guise",
       linetype = "Word", title="AI Raising Perception: By Word") +
  coord_cartesian(ylim=c(0, 1)) +
  scale_x_continuous(breaks = -3:3) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,4,3)])+
  gg_theme()
byWord_prop_plot


# AU
byWord_prop_plot <- condition.means %>% filter(vowel=="AU") %>%
  ggplot(aes(x = step, y = grandM.Prop)) +
  geom_point(stat="identity", aes(colour = factor(speakerGuise)), cex=5, alpha=0.75) +
  geom_line(aes(colour=factor(speakerGuise), linetype=factor(word)), lwd=1) +
  geom_errorbar(width = .25, aes(ymin = grandM.Prop-se, ymax = grandM.Prop+se, colour = factor(speakerGuise))) +
  facet_grid(speaker~word) +
  labs(y = "Proportion 'raised' response", x = "Continuum Step (UR to RS)", color="Guise",
       linetype = "Word", title="AU Raising Perception: By Word") +
  coord_cartesian(ylim=c(0, 1)) +
  scale_x_continuous(breaks = -3:3) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,4,3)])+
  gg_theme()
byWord_prop_plot

PropRS by Individual

(adapted from CantoMergers project)

Questionnaire Data

Pre-Process Data

Read & Clean Results

The Qualtrics output includes a text response file and a numerical response file. Because I want to use text for some questions (e.g. IK2, the word selection question) but numbers for other questions (e.g. familiarity scale questions), I need to work with both.

I have downloaded the files and renamed them as ’_text’ and ’_num" files. Specifically, file names have been renamed from the original Qualtrics download name to final_lbq_num.csv and final_lbq_text.csv.

length(unique(quesdata.read$subjID))
[1] 136

Remove the unnecessarily columns and rows. Also extract the question text while we’re at it (before removing the rows) so that we can refer to the questions as necessary, but they won’t be in the data to be analysed.

# Remove metadata columns (first several)
quesdata <- quesdata.read %>% 
  select(-c(StartDate, EndDate, Status, IPAddress, Progress, Duration..in.seconds., Finished, RecordedDate, ResponseId, RecipientLastName, RecipientFirstName, RecipientEmail, ExternalReference, LocationLatitude, LocationLongitude, DistributionChannel, UserLanguage, PROLIFIC_PID))

# Question reference (if want to look back at question text)
questions <- quesdata %>% slice(1)

# Remove unecessary question header and test data rows + add/fix relevant info + remove data from dropped subjects
quesdata <- quesdata %>%
  # Remove irrelevant rows and removed data
  subset(subjID != "Please check that your Prolific ID is correct, then press the 'next' button to continue with the survey." & subjID !="{\"ImportId\":\"QID78_TEXT\"}") %>%
  subset(subjID != "preview1") %>%

  # Merge with perception data (for convenience, uses the minimal dataframe `condata.tones`)
  ## Adds subject and condition info + automatically drops subjects that were screened out based on perception experiment performance/returns
  rename(participantId = subjID) %>%
  merge(., condata.tones) %>%
  mutate(guiseCombination = case_when(conditionId == "condA" | conditionId == "condB" ~ "match",
                                        conditionId == "condC" | conditionId == "condD" ~ "mismatch",
                                        conditionId == "condE" | conditionId == "condF" ~ "baseline")) %>%
  mutate(speakerOrder = case_when(conditionId == "condA" | conditionId == "condC" | conditionId == "condE" ~ "S3-S9",
                                  conditionId == "condB" | conditionId == "condD" | conditionId == "condF" ~ "S9-S3")) %>%
  droplevels()

# Fix answers from the one 'alien' participant
quesdata <- quesdata %>%
  mutate(Gender = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'm', Gender),
         LingExp = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'none', LingExp),
         Loc1_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '0', Loc1_1),
         Loc1_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '1', Loc1_2),
         Loc1_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'zeeland', Loc1_3),
         Loc1_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'ottawa', Loc1_4),
         Loc1_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'mi', Loc1_5),
         Loc2_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '1', Loc2_1),
         Loc2_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '2', Loc2_2),
         Loc2_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'sedona', Loc2_3),
         Loc2_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '', Loc2_4),
         Loc2_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'arizona', Loc2_5),
         Loc3_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '2', Loc3_1),
         Loc3_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '14', Loc3_2),
         Loc3_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'glenn', Loc3_3),
         Loc3_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'allegan', Loc3_4),
         Loc3_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'mi', Loc3_5),
         Loc4_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '14', Loc4_1),
         Loc4_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '18', Loc4_2),
         Loc4_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'south haven', Loc4_3),
         Loc4_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'van buren', Loc4_4),
         Loc5_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'mi', Loc4_5),
         Loc5_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '18', Loc5_1),
         Loc5_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '23', Loc5_2),
         Loc5_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'grand rapids', Loc5_3),
         Loc5_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'kent', Loc5_4),
         Loc5_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'mi', Loc5_5),
         Loc6_1 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '23', Loc6_1),
         Loc6_2 = ifelse(participantId=='5fc511c32b9bc60bc189b893', '34', Loc6_2),
         Loc6_3 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'various... grandville', Loc6_3),
         Loc6_4 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'various... kent', Loc6_4),
         Loc6_5 = ifelse(participantId=='5fc511c32b9bc60bc189b893', 'various... mi', Loc6_5))

Question Wording

Here’s the table of the question tags, numbers and text. This interactive table allows for sorting and searching! We can use this to check the exact wording of the questions—all of them as they were shown to the participant.

# Print questions for reference
DT::datatable(questions, 
              options=list(scrollX = TRUE,
                           autoWidth = TRUE,
                           columnDefs = list(list(width = '200px', targets = "_all"))))

Process Demographic Info

# Subset to demographic question columns
quesdata.demo <- quesdata %>% filter(dataFormat == "text") %>%
  select(conditionId, participantId, guiseCombination, speakerOrder, 
         Age, Gender, Ethnicity, SpHDisorder, SpHDisorder_2_TEXT, Degree, LingExp, FirstLang, 
         Loc1_1:Loc6_5) %>%
  
  # Fix spelling errors and variation on demographic questions
  mutate_at(vars(-conditionId, -speakerOrder),tolower) %>%
  mutate_if(is.character, str_trim)

# Check data
quesdata.demo

# Summary
# summary(quesdata.demo)

Check Values

# Quick check via table
ques.demo.sum <- quesdata.demo %>%  select(conditionId, Age, Gender, Ethnicity, Loc1_5)
with(quesdata.demo, unique(Gender))
[1] "male"         "female"       "m"            "non binary"   "woman/female" "cis male"     "nonbinary"   
with(quesdata.demo, unique(Loc1_5))
 [1] "michigan"      "mi"            "ca"            "usa"           "michigan/usa"  "michigan/us"   "mi/"           "mi, us"       
 [9] ""              "mi/usa"        "ca/usa"        "texas"         "michigan usa"  "michigan, usa"
with(quesdata.demo, unique(Ethnicity))
 [1] "white"             "asian"             "caucasian"         "white/hispanic"    "african american"  "white/caucasian"   "american"         
 [8] "caucasian/white"   "black/usa"         "caucasion"         "hispanic"          "white american"    "african-american"  "middle-eastern"   
[15] "sicilian/american" "middle easten"     "black"             "asian/caucasian"   "mezzogiorno"       "east asian"       

Clean Free Responses

quesdata.demo <- quesdata.demo %>%
  
  mutate(Gender = mgsub(Gender, c("woman.*|female|cis female", "male|cis male", "non-binary|non binary|nonbinary"), c("f", "m", "nb"))) %>%
  
  mutate_at(vars(starts_with("Loc") & ends_with("_5")), ~ mgsub(.x, c("michigan.*", "mi.*"), c("mi", "mi"))) %>%
  
  mutate(Ethnicity = mgsub(Ethnicity, c("caucasian|caucasion|american|mezzogiorno","african american|african-american", "middle easten"), c("white", "black", "middle-eastern"))) %>%
  mutate(Ethnicity = mgsub(Ethnicity, c("asian/white","white/hispanic"), c("multiracial","multiracial"), fixed=TRUE)) %>%
  mutate(Ethnicity = mgsub(Ethnicity, c(".*white.*", "black.*", ".*asian"), c("white", "black", "asian"))) %>%
  
  # Change vector classes from character class
  mutate_at(vars(Age), as.numeric) %>%
  
  # Select
  select(conditionId, participantId, everything())
  
quesdata.demo

Descriptive Stats

# Ethnicity Counts
quesdata.demo %>% group_by(Ethnicity) %>% count() 

quesdata.demo %>% group_by(Ethnicity, conditionId) %>% count() %>% pivot_wider(Ethnicity, names_from = conditionId, values_from=n)
NA
# Gender Counts
quesdata.demo %>% group_by(Gender) %>% count()

quesdata.demo %>% group_by(Gender, conditionId) %>% count() %>% pivot_wider(Gender, names_from = conditionId, values_from=n)
NA
# Age
quesdata.demo %>% summarise(n=length(conditionId), minAge = min(Age), maxAge = max(Age), meanAge = mean(Age), sdAge = sd(Age))

quesdata.demo %>% group_by(conditionId) %>%
  summarise(n=length(conditionId), minAge = min(Age), maxAge = max(Age), meanAge = mean(Age), sdAge = sd(Age)) %>% ungroup()
NA

Locations List (under dev)

quesdata.loc <- quesdata.demo %>% select(participantId, starts_with("Loc"))
quesdata.loc

loc.code <- data.frame(Loc = unique((quesdata.loc$Loc1_3))) %>%
  rbind(data.frame(Loc = unique((quesdata.loc$Loc2_3)))) %>%
  rbind(data.frame(Loc = unique((quesdata.loc$Loc3_3)))) %>%
  rbind(data.frame(Loc = unique((quesdata.loc$Loc4_3)))) %>%
  rbind(data.frame(Loc = unique((quesdata.loc$Loc5_3)))) %>%
  rbind(data.frame(Loc = unique((quesdata.loc$Loc6_3)))) %>%
  unique()
loc.code

Process Text Responses

# Subset data to text format
quesdata.text <- quesdata %>% filter(dataFormat == "text") %>%
  select(participantId, conditionId, guiseCombination, speakerOrder, everything()) %>%
  select(-c(Age, Gender, Ethnicity, SpHDisorder, SpHDisorder_2_TEXT, Degree, LingExp, FirstLang, 
         Loc1_1:Loc6_5, expPurpose)) %>%
  mutate_if(is.character, as.factor)

quesdata.text.sub <- quesdata.text %>% 
  select(-starts_with("Q"), -SC0, -tonesCorrect) %>%
  droplevels()
quesdata.text.sub

IK2: Word Selection Question

IK2 (Implicit Knowledge Q2) refers to the question where subjects select all the words they think Canadians and Michiganders would pronounce differently (“Which of the following words, if any, do you think would be pronounced differently by someone from Canada, as opposed to someone from Michigan? Please select all that apply.”). The question includes 30 words, 5 of which are target /au/ words and 5 of which are target /ai/ words.

We want to go from the raw data, the selected words, to the number of target words (/au/ and /ai/ raising words) selected. The response format is words separated by commas in one cell. So, we need to separate the strings into separate words, check whether each target word occured, then tabulate the scores. A simple search for word strings won’t work, because some words (e.g. out) are substrings of other words (e.g. about).

My solution was to first separate the single column by commas into multiple columns, one per word. To check whether the word was a response, I implement a count of 1 in a new column if a search function finds the target string in a row. This is done for each target word. Finally, I sum the count columns for /ai/ and /au/ separately to get a score out of 5.

# IK2: Can Word Selection question

# Select only IK2 question + create copied column of Words (for next step)
ik2 <- select(quesdata.text.sub, participantId, IK2.CanWords)
ik2 <- mutate(ik2, Words = IK2.CanWords)

# Separate Words into columns (by comma) + create new columns of word in list per row
# Number of columns must be the same for every row, so find the max number of words selected by any subject (23 here)
# Each subject will have 23 columns, one word per column (Word_1, Word_2...) until no more words (NA if no word)
ik2 <- separate(ik2, "Words", paste("Word", 1:30, sep="_"), sep=",", extra="drop")

# Identify target words in each subjects' responses
# Count 1 if word exists in row; 0 if none
# /au/ targets
ik2 <- mutate(ik2, IK2.au.out = as.integer(apply(ik2, 1, function(x) any(x %in% "out"))),
              IK2.au.about = as.integer(grepl('about',IK2.CanWords)),
              IK2.au.devout = as.integer(grepl('devout',IK2.CanWords)),
              IK2.au.house = as.integer(grepl('house',IK2.CanWords)),
              IK2.au.pouch = as.integer(grepl('pouch',IK2.CanWords)))

# /ai/ targets
ik2 <- mutate(ik2, IK2.ai.like = as.integer(apply(ik2, 1, function(x) any(x %in% "like"))),
              IK2.ai.right = as.integer(apply(ik2, 1, function(x) any(x %in% "right"))),
              IK2.ai.might = as.integer(apply(ik2, 1, function(x) any(x %in% "might"))),
              IK2.ai.unite = as.integer(grepl('unite',IK2.CanWords)),
              IK2.ai.ripe = as.integer(apply(ik2, 1, function(x) any(x %in% "ripe"))))

# Sum of targets selected for /au/ and /ai/
ik2 <- mutate(ik2, IK2.au = rowSums(select(ik2, IK2.au.out:IK2.au.pouch)),
              IK2.ai = rowSums(select(ik2, IK2.ai.like:IK2.ai.ripe)))

# Here are two versions of the data
# ...with score for each target word + sum of /au/ and /ai/ targets selected
ik2.values <- select(ik2, participantId, IK2.au, IK2.ai, IK2.au.out:IK2.ai.ripe)

# ...with only sum of /au/ and /ai/ targets selected
ik2.sum <- select(ik2, participantId, IK2.au:IK2.ai)

Process Num Responses

A few cases where participants do not respond to a question because the answer is ‘no’ results in an ‘NA’ entry. These ’NA’s for specific columns are adjusted “manually” to the correct value.

# Subset data to num format
quesdata.num <- quesdata %>% subset(dataFormat == "num") %>%
  select(participantId, conditionId, guiseCombination, speakerOrder, everything()) %>%
  select(-c(Age, Gender, Ethnicity, SpHDisorder, SpHDisorder_2_TEXT, Degree, LingExp, FirstLang, 
         Loc1_1:Loc6_5, expPurpose)) %>%
  rename(EQ.raws = SC0) %>%
  mutate_at(vars(participantId:speakerOrder), as.factor) %>%
  mutate_if(~ all(grepl('^\\d+$', .x)), as.numeric)

## Further adjustments
quesdata.num.sub <- quesdata.num %>%
  # Convert columns to numeric, leaving non-numeric columns as NA
  mutate_if(is.character, as.numeric) %>%
  # Remove columns that are all NA (specifically, if Sum of that column's NAs is NOT equal to the number of rows, select )
  select_if(colSums(is.na(.)) != nrow(.)) %>%
  # Remove columns of certain types of questions or specific question number
  select(-starts_with("Q"), -starts_with("Lang"), -starts_with("IK2"), -starts_with("ME1"), -ends_with("TEXT")) %>%

  # Adjust values of NA (for cases where they can be interpreted as a 'no' or 'never')
  mutate(Travel.NE_Visits = coalesce(Travel.NE_Visits, 1), Travel.S_Visits = coalesce(Travel.S_Visits, 1),
         Travel.W_Visits = coalesce(Travel.W_Visits, 1), Travel.Can_Visits = coalesce(Travel.Can_Visits, 1),
         Travel.NE_Time = coalesce(Travel.NE_Time, 1), Travel.S_Time = coalesce(Travel.S_Time, 1),
         Travel.W_Time = coalesce(Travel.W_Time, 1), Travel.Can_Time = coalesce(Travel.Can_Time, 1),
         PE1.Relatives = coalesce(PE1.Relatives, 2), PE1.CloseFamFriends = coalesce(PE1.CloseFamFriends, 2),
         EK3.CanAI.Diff = coalesce(EK3.CanAI.Diff, 2), EK4.CanAU.Diff = coalesce(EK4.CanAU.Diff, 2),
         ME3.Sources.OtherMedia_1 = coalesce(ME3.Sources.OtherMedia_1, 1), 
         ME3.Sources.Other_1 = coalesce(ME3.Sources.Other_1, 1),
         ME3.Sources.News_1 = coalesce(ME3.Sources.News_1, 1)) %>%
  droplevels()
Problem with `mutate()` input `Lang1_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang1_1` is `.Primitive("as.double")(Lang1_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang1_2`.
ℹ NAs introduced by coercion
ℹ Input `Lang1_2` is `.Primitive("as.double")(Lang1_2)`.NAs introduced by coercionProblem with `mutate()` input `Lang2_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang2_1` is `.Primitive("as.double")(Lang2_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang2_2`.
ℹ NAs introduced by coercion
ℹ Input `Lang2_2` is `.Primitive("as.double")(Lang2_2)`.NAs introduced by coercionProblem with `mutate()` input `Lang3_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang3_1` is `.Primitive("as.double")(Lang3_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang4_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang4_1` is `.Primitive("as.double")(Lang4_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang5_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang5_1` is `.Primitive("as.double")(Lang5_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang6_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang6_1` is `.Primitive("as.double")(Lang6_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang7_1`.
ℹ NAs introduced by coercion
ℹ Input `Lang7_1` is `.Primitive("as.double")(Lang7_1)`.NAs introduced by coercionProblem with `mutate()` input `Lang7_2`.
ℹ NAs introduced by coercion
ℹ Input `Lang7_2` is `.Primitive("as.double")(Lang7_2)`.NAs introduced by coercionProblem with `mutate()` input `LangSpeakFreq`.
ℹ NAs introduced by coercion
ℹ Input `LangSpeakFreq` is `.Primitive("as.double")(LangSpeakFreq)`.NAs introduced by coercionProblem with `mutate()` input `LangHearFreq`.
ℹ NAs introduced by coercion
ℹ Input `LangHearFreq` is `.Primitive("as.double")(LangHearFreq)`.NAs introduced by coercionProblem with `mutate()` input `SK1.WhereStandard`.
ℹ NAs introduced by coercion
ℹ Input `SK1.WhereStandard` is `.Primitive("as.double")(SK1.WhereStandard)`.NAs introduced by coercionProblem with `mutate()` input `IK2.CanWords`.
ℹ NAs introduced by coercion
ℹ Input `IK2.CanWords` is `.Primitive("as.double")(IK2.CanWords)`.NAs introduced by coercionProblem with `mutate()` input `EK1.CanSpeak.Diff`.
ℹ NAs introduced by coercion
ℹ Input `EK1.CanSpeak.Diff` is `.Primitive("as.double")(EK1.CanSpeak.Diff)`.NAs introduced by coercionProblem with `mutate()` input `EK2.CanPron.Diff`.
ℹ NAs introduced by coercion
ℹ Input `EK2.CanPron.Diff` is `.Primitive("as.double")(EK2.CanPron.Diff)`.NAs introduced by coercionProblem with `mutate()` input `EK3.CanAI.Diff_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `EK3.CanAI.Diff_1_TEXT` is `.Primitive("as.double")(EK3.CanAI.Diff_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `EK4.CanAU.Diff_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `EK4.CanAU.Diff_1_TEXT` is `.Primitive("as.double")(EK4.CanAU.Diff_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `PE1.Relatives_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `PE1.Relatives_1_TEXT` is `.Primitive("as.double")(PE1.Relatives_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `PE1.CloseFamFriends_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `PE1.CloseFamFriends_1_TEXT` is `.Primitive("as.double")(PE1.CloseFamFriends_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `ME1.TVShows`.
ℹ NAs introduced by coercion
ℹ Input `ME1.TVShows` is `.Primitive("as.double")(ME1.TVShows)`.NAs introduced by coercionProblem with `mutate()` input `ME3.Sources.OtherMedia_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `ME3.Sources.OtherMedia_1_TEXT` is `.Primitive("as.double")(ME3.Sources.OtherMedia_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `ME3.Sources.Other_1_TEXT`.
ℹ NAs introduced by coercion
ℹ Input `ME3.Sources.Other_1_TEXT` is `.Primitive("as.double")(ME3.Sources.Other_1_TEXT)`.NAs introduced by coercionProblem with `mutate()` input `dataFormat`.
ℹ NAs introduced by coercion
ℹ Input `dataFormat` is `.Primitive("as.double")(dataFormat)`.NAs introduced by coercion
quesdata.num.sub

Finalize Cleaned Results

# Select and Merge only relavant numerical columns of data for PCA analysis
quesdata.clean <- quesdata.demo %>%
  select(participantId, conditionId, guiseCombination, speakerOrder, Age, Gender, Ethnicity) %>%
  merge(., ik2.values) %>%
  merge(., quesdata.num.sub) %>%
  droplevels()

# quesdata.final
quesdata.clean

Analyze Data

PCA: Awareness & Experience

# Check correlations, which motivate the use of PCA to reduce dimensionality
with(quesdata.clean, cor.test(IK2.ai, IK2.au))

# Test correlations of similar questions
with(quesdata.clean, cor.test(Travel.Can_Visits, Travel.Can_Time))
with(quesdata.clean, cor.test(SE1.Fam.oot_1, SE2.Freq.Overall_1))
with(quesdata.clean, cor.test(SE2.Freq.Overall_1, SE2.Freq.Recent_1))
with(quesdata.clean, cor.test(SE2.Freq.Overall_1, SE2.Freq.Child_1))
with(quesdata.clean, cor.test(SE2.Freq.Recent_1, SE2.Freq.Child_1))

Run PCA

Packages required for this PCA: * PCA command from FactoMineR library (see index for more info) * paran command from paran library * Varimax command from GPArotations library (https://stats.stackexchange.com/questions/59213/how-to-compute-varimax-rotated-principal-components-in-r)

Packages for visualization of PCA * fviz_pca_ind and fviz_pca_biplot from factoextra

# (0) Select data for PCA — only numerical columns
# names(quesdata.clean)

# Medium trimmed set — removes general Canadian stereotype experience/knowledge, imitation, Sources of CE, hockey
quesdata.pca.med <- select(quesdata.clean, 
                           IK2.au, IK2.ai, 
                           SE1.Fam.oot_1, SE2.Freq.Recent_1:SE2.Freq.Overall_1, 
                           PE2.CanSpeakFreq.Recent_1:PE2.CanSpeakFreq.Overall_1,
                           ME4.CanHearFreq.Recent_1:ME4.CanHearFreq.Overall_1)
quesdata.pca.med
NA
## (1) Run Parallel Analysis with `paran`
# Standard way to decide on the number of factors or components needed in an FA or PCA.
# Prints out a scree plot as well, with the randomized line + unadjusted line
paran(quesdata.pca.med,
      graph = TRUE, color = TRUE, 
      col = c("black", "red", "blue"), lty = c(1, 2, 3), lwd = 1, legend = TRUE, 
      file = "", width = 640, height = 640, grdevice = "png", seed = 0)

Using eigendecomposition of correlation matrix.
Computing: 10%  20%  30%  40%  50%  60%  70%  80%  90%  100%


Results of Horn's Parallel Analysis for component retention
360 iterations, using the mean estimate

-------------------------------------------------- 
Component   Adjusted    Unadjusted    Estimated 
            Eigenvalue  Eigenvalue    Bias 
-------------------------------------------------- 
1           4.923608    5.569209      0.645600
2           1.014315    1.477026      0.462710
-------------------------------------------------- 

Adjusted eigenvalues > 1 indicate dimensions to retain.
(2 components retained)

## (2) Run PCA with `FactoMineR`
# ncp = number of components; adjust after checking the parallel analysis output

# FactoMineR PCA Commands
#plbqPCA        # lists commands
#plbqPCA$var    # variables
#plbqPCA$ind    # individuals
#plbqPCA$call   # summary stats

lbqPCA <- PCA(quesdata.pca.med, scale.unit = T, ncp =2, graph=F)

## Relevant Raw PCA Output
# Eigenvalues & percent variance accounted for
(eigenvalues <- lbqPCA$eig)
        eigenvalue percentage of variance cumulative percentage of variance
comp 1  5.56920934             46.4100778                          46.41008
comp 2  1.47702633             12.3085527                          58.71863
comp 3  1.17513102              9.7927585                          68.51139
comp 4  1.08602655              9.0502212                          77.56161
comp 5  0.77331255              6.4442713                          84.00588
comp 6  0.64543605              5.3786337                          89.38452
comp 7  0.52683655              4.3903046                          93.77482
comp 8  0.29948597              2.4957164                          96.27054
comp 9  0.18393724              1.5328103                          97.80335
comp 10 0.13160788              1.0967324                          98.90008
comp 11 0.09079792              0.7566494                          99.65673
comp 12 0.04119260              0.3432717                         100.00000
# Eigenvectors (=Factor matrix, factor score coefficients; sometimes called the factor, but NOT factor scores)
(eigenvectors <- lbqPCA$var$coord)
                                Dim.1       Dim.2
IK2.au                      0.3447922  0.42338649
IK2.ai                     -0.1448258 -0.29672009
SE1.Fam.oot_1               0.5141354  0.29038508
SE2.Freq.Recent_1           0.7477153  0.29500278
SE2.Freq.Child_1            0.6827982  0.47880752
SE2.Freq.Overall_1          0.7706314  0.41458997
PE2.CanSpeakFreq.Recent_1   0.7657419 -0.49904588
PE2.CanSpeakFreq.Child_1    0.6253158 -0.05611207
PE2.CanSpeakFreq.Overall_1  0.8219899 -0.44469439
ME4.CanHearFreq.Recent_1    0.7866714 -0.32268452
ME4.CanHearFreq.Child_1     0.7453443  0.08655559
ME4.CanHearFreq.Overall_1   0.8475941 -0.27508210
# Factor loadings (eigenvectors scaled by the square root of their associated eigenvalues)
# Calculate factor loadings using the output eigenvectors and eigenvalues
rawLoadings <- sweep(lbqPCA$var$coord,2,sqrt(lbqPCA$eig[1:ncol(lbqPCA$var$coord),1]),FUN="/")
rawLoadings
                                 Dim.1       Dim.2
IK2.au                      0.14610351  0.34837171
IK2.ai                     -0.06136901 -0.24414781
SE1.Fam.oot_1               0.21786163  0.23893522
SE2.Freq.Recent_1           0.31683962  0.24273477
SE2.Freq.Child_1            0.28933140  0.39397335
SE2.Freq.Overall_1          0.32655016  0.34113374
PE2.CanSpeakFreq.Recent_1   0.32447830 -0.41062592
PE2.CanSpeakFreq.Child_1    0.26497363 -0.04617024
PE2.CanSpeakFreq.Overall_1  0.34831302 -0.36590431
ME4.CanHearFreq.Recent_1    0.33334703 -0.26551191
ME4.CanHearFreq.Child_1     0.31583494  0.07121984
ME4.CanHearFreq.Overall_1   0.35916263 -0.22634360
# Factor scores for each subject and dimension (also: Individual coordinate scores; principle coordinates)
rawScores <- lbqPCA$ind$coord
## (3) Conduct rotation on the PCA factor loadings with `GPArotation`
# Rotations are typically done on the retained component factor loadings, not on all components nor on the eigenvectors
# Performed for ease of interpretation, maximizing factor loadings
(rotLoadings <- Varimax(rawLoadings)$loadings)
                                  Dim.1       Dim.2
IK2.au                     -0.130050509  0.35467725
IK2.ai                      0.121199839 -0.22064656
SE1.Fam.oot_1              -0.002994765  0.32333383
SE2.Freq.Recent_1           0.066937539  0.39348047
SE2.Freq.Child_1           -0.056147458  0.48556681
SE2.Freq.Overall_1          0.007083407  0.47218329
PE2.CanSpeakFreq.Recent_1   0.517200961 -0.08001862
PE2.CanSpeakFreq.Child_1    0.225560983  0.14650924
PE2.CanSpeakFreq.Overall_1  0.504227143 -0.03103091
ME4.CanHearFreq.Recent_1    0.424936553  0.03233799
ME4.CanHearFreq.Child_1     0.182931745  0.26713284
ME4.CanHearFreq.Overall_1   0.417193540  0.07860512
# Recover Rotation matrix from loadings
# Because the rotLoadings are calculated from rawLoadings %*% rotMatrix, can recover rotMatrix by rotLoadings "divided" by rawLoadings, which in matrix multiplication is multiplying by the inverse (transpose) 
# Note: For some reason, can't call Varimax(rawLoadings)$rotmat (just get NULL); this recreates the same matrix from Varimax(rawLoadings)
(rotMatrixL <- t(rawLoadings) %*% rotLoadings)
           Dim.1     Dim.2
Dim.1  0.7326701 0.6805839
Dim.2 -0.6805839 0.7326701
# Calculate rotated factor scores
# The formula simply multiplies the normalized variable scores with the rotation matrix to get rotated factor scores
# First, z-score the raw scores using base R scale()
# Then, matrix multiply the matrix of zScores with the rotation matrix
# Result is a matrix with columns=components and rows=each subject
zScores <- scale(rawScores)
rotScores <- zScores %*% rotMatrixL

Plot PCA

## (4) Data Visualization of Raw Scores with `factoextra`

# Plot individual factor scores
fviz_pca_ind(lbqPCA, col.ind = "#00AFBB", repel = TRUE)


# Biplot, including individual scores and factor vectors
fviz_pca_biplot(lbqPCA, label = "all", col.ind = "#00AFBB", col.var="black", ggtheme = theme_minimal())

## (5) Manual Plots of Rotated Scores with `ggplot`

## Create dataframes of the rotated factor loading and factor score matrices
# Convert rotated factor loadings matrix to data frame; add variable number
rotLoadingsData <- as.data.frame(rotLoadings)
rotLoadingsData <- mutate(rotLoadingsData, variable = row.names(rotLoadings))
rotLoadingsData <- mutate(rotLoadingsData, variable = factor(variable))

# Convert rotated factor score matrix to data frame; add subject number
rotScoreData <- as.data.frame(rotScores)
rotScoreData <- rotScoreData %>% mutate(subject = 1:nrow(.))

## Create base plots
# Loading plot
loadingplot <- rotLoadingsData %>% ggplot(aes(x=Dim.1, y=Dim.2))+
  geom_segment(data=rotLoadingsData, mapping=aes(x=0, y=0, xend=Dim.1*4, yend=Dim.2*4), arrow=arrow(), size=0.5, color="black") +
  geom_text(data=rotLoadingsData, aes(x=Dim.1*4, y=Dim.2*4, label=variable), color="red",check_overlap=T) +
  scale_x_continuous(lim=c(-2.75, 2.75),breaks=seq(-3,3,1)) +
  scale_y_continuous(lim=c(-3.5, 3.5),breaks=seq(-3,3,1)) +
  geom_hline(yintercept=0, linetype="dashed") +
  geom_vline(xintercept=0, linetype="dashed") +
  labs(title="Variables - PCA", x="Dim 1 (36.4%)", y="Dim 2 (17.7%)") +
  gg_theme()
loadingplot


# Scatter plot of Individual factor scores
dimplot = ggplot(rotScoreData, aes(x=Dim.1, y=Dim.2))+
  geom_point(na.rm=TRUE, color="#00AFBB") +
  geom_text(aes(label=subject),hjust=1.5,vjust=1.5, color="#00AFBB", check_overlap=T)+
  scale_x_continuous(lim=c(-2.75, 2.75),breaks=seq(-3,3,1)) +
  scale_y_continuous(lim=c(-3.5, 3.5),breaks=seq(-3,3,1)) +
  geom_hline(yintercept=0, linetype="dashed") +
  geom_vline(xintercept=0, linetype="dashed") +
  labs(title="Individuals - PCA", x="Dim 1 (36.4%)", y="Dim 2 (17.7%)") +
  gg_theme()
dimplot


## Merge loading and score plot = Biplot

# Biplot of factor loadings + ind factor scores
ggplot(rotScoreData, aes(x=Dim.1, y=Dim.2))+
  geom_point(na.rm=TRUE, color="#00AFBB") +
  geom_text(aes(label=subject),hjust=1.5,vjust=1.5, color="#00AFBB", check_overlap=T)+
  
  # Overlay loading plot (i.e. arrows)
  geom_segment(data=rotLoadingsData, mapping=aes(x=0, y=0, xend=Dim.1*4, yend=Dim.2*4), arrow=arrow(), size=0.5, color="black") +
  geom_text(data=rotLoadingsData, aes(x=Dim.1*4.5, y=Dim.2*4.5, label=variable), color="red",check_overlap=T, nudge_y = 0)+

  scale_x_continuous(lim=c(-2.75, 2.75),breaks=seq(-3,3,1)) +
  scale_y_continuous(lim=c(-3.5, 3.5),breaks=seq(-3,3,1)) +
  geom_hline(yintercept=0, linetype="dashed") +
  geom_vline(xintercept=0, linetype="dashed") +
  labs(title="Biplot - PCA", x="Dim 1 (36.4%)", y="Dim 2 (17.7%)") +
  gg_theme()

Interpret PCA

## Interpret PCs (Dimensions) based on factor loadings
rotLoadings.df <- as.data.frame(rotLoadings) %>%
  rownames_to_column(., "Variables") %>%
  rename(., "PC1"= "Dim.1", "PC2" = "Dim.2")
rotLoadings.df
# PC1 Only Contributors
rotLoadings.df %>% filter(abs(PC1) > 0.2)
# PC2 Only Contributors
rotLoadings.df %>% filter(abs(PC2) > 0.2)
# Check for overlapping contributors
rotLoadings.df %>% filter(abs(PC2) > 0.2 & abs(PC1) > 0.2)

# Check for non-contributors
rotLoadings.df %>% filter(abs(PC2) < 0.2 & abs(PC1) < 0.2)

Finalize Results w/ PCA

# For Merging: Convert rotated factor score matrix to data frame; add participantId (assuming order of input dataframe)
There were 13 warnings (use warnings() to see them)
indPCAdata <- rotScoreData %>%
  mutate(participantId = quesdata.clean$participantId) %>%
  rename(CEscore = Dim.1) %>%
  rename(SAscore = Dim.2)

# Merge participant data with PC scores 
# Only select the main relevant scores
quesdata.final <- quesdata.clean %>%
  merge(., indPCAdata) %>%
  mutate(MSscore = scale(SK2.MichvStand)) %>%
  mutate(EQscore = scale(EQ.raws)) %>%
  mutate_at(vars(Age), as.numeric) %>%
  mutate_if(is.character, as.factor) %>%
  select(participantId, conditionId, guiseCombination, speakerOrder, Age, Gender, Ethnicity, CEscore, SAscore, MSscore, EQscore, EQ.raws, everything())

summary(quesdata.final)
                  participantId conditionId guiseCombination speakerOrder      Age        Gender           Ethnicity     CEscore       
 56d9f54b610fc0000bb76e8d: 1    condA:32    baseline:31      S3-S9:90     Min.   :18.00   f :31   asian         : 6   Min.   :-1.6202  
 56e72067c89073000be77fdf: 1    condC:27    match   :32                   1st Qu.:22.00   m :56   black         : 9   1st Qu.:-0.7392  
 59c5689f46f72100019066a7: 1    condE:31    mismatch:27                   Median :27.00   nb: 3   hispanic      : 2   Median :-0.2443  
 5a412f0b99311d0001df431e: 1                                              Mean   :29.78           middle-eastern: 2   Mean   : 0.0000  
 5b14791ebd9c31000156e8ab: 1                                              3rd Qu.:33.00           multiracial   : 2   3rd Qu.: 0.6814  
 5bef64b373e44f0001092811: 1                                              Max.   :68.00           white         :69   Max.   : 2.6322  
 (Other)                 :84                                                                                                           
    SAscore              MSscore.V1           EQscore.V1         EQ.raws          IK2.au          IK2.ai         IK2.au.out    
 Min.   :-2.73135   Min.   :-2.9560813   Min.   :-2.2862673   Min.   :12.00   Min.   :0.000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:-0.53157   1st Qu.:-0.3979340   1st Qu.:-0.6133888   1st Qu.:33.00   1st Qu.:2.000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median : 0.09743   Median : 0.4547817   Median :-0.1752539   Median :38.50   Median :4.000   Median :0.0000   Median :1.0000  
 Mean   : 0.00000   Mean   : 0.0000000   Mean   : 0.0000000   Mean   :40.70   Mean   :3.344   Mean   :0.9333   Mean   :0.6556  
 3rd Qu.: 0.71486   3rd Qu.: 0.4547817   3rd Qu.: 0.7209310   3rd Qu.:49.75   3rd Qu.:5.000   3rd Qu.:1.0000   3rd Qu.:1.0000  
 Max.   : 1.79057   Max.   : 1.3074975   Max.   : 2.3340639   Max.   :70.00   Max.   :5.000   Max.   :5.0000   Max.   :1.0000  
                                                                                                                               
  IK2.au.about    IK2.au.devout     IK2.au.house     IK2.au.pouch     IK2.ai.like      IK2.ai.right     IK2.ai.might     IK2.ai.unite   
 Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :1.0000   Median :1.0000   Median :1.0000   Median :1.0000   Median :0.0000   Median :0.0000   Median :0.0000   Median :0.0000  
 Mean   :0.7778   Mean   :0.7333   Mean   :0.5889   Mean   :0.5889   Mean   :0.1444   Mean   :0.3222   Mean   :0.1889   Mean   :0.1444  
 3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
                                                                                                                                        
  IK2.ai.ripe     Travel.NE_Visits Travel.S_Visits Travel.W_Visits Travel.Can_Visits Travel.NE_Time  Travel.S_Time   Travel.W_Time  
 Min.   :0.0000   Min.   :1.000    Min.   :1.0     Min.   :1.000   Min.   :1.000     Min.   :1.000   Min.   :1.000   Min.   :1.000  
 1st Qu.:0.0000   1st Qu.:1.000    1st Qu.:2.0     1st Qu.:1.000   1st Qu.:1.000     1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.000  
 Median :0.0000   Median :2.000    Median :2.0     Median :1.500   Median :2.000     Median :1.000   Median :1.000   Median :1.000  
 Mean   :0.1333   Mean   :1.811    Mean   :2.3     Mean   :1.678   Mean   :1.911     Mean   :1.356   Mean   :1.556   Mean   :1.222  
 3rd Qu.:0.0000   3rd Qu.:2.000    3rd Qu.:3.0     3rd Qu.:2.000   3rd Qu.:2.000     3rd Qu.:1.000   3rd Qu.:2.000   3rd Qu.:1.000  
 Max.   :1.0000   Max.   :4.000    Max.   :4.0     Max.   :4.000   Max.   :4.000     Max.   :4.000   Max.   :4.000   Max.   :4.000  
                                                                                                                                    
 Travel.Can_Time SK2.MichvStand  SK3.CanvStand   IK1.DecideCanada  EK1.CanSpeak    EK2.CanPron      EK3.CanAI     EK3.CanAI.Diff 
 Min.   :1.000   Min.   :2.000   Min.   :2.000   Min.   :2.000    Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
 1st Qu.:1.000   1st Qu.:5.000   1st Qu.:4.000   1st Qu.:2.000    1st Qu.:1.000   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:2.000  
 Median :1.000   Median :6.000   Median :4.000   Median :3.000    Median :1.000   Median :1.000   Median :2.000   Median :2.000  
 Mean   :1.167   Mean   :5.467   Mean   :4.322   Mean   :3.278    Mean   :1.067   Mean   :1.067   Mean   :1.689   Mean   :1.767  
 3rd Qu.:1.000   3rd Qu.:6.000   3rd Qu.:5.000   3rd Qu.:4.000    3rd Qu.:1.000   3rd Qu.:1.000   3rd Qu.:2.000   3rd Qu.:2.000  
 Max.   :4.000   Max.   :7.000   Max.   :7.000   Max.   :5.000    Max.   :2.000   Max.   :2.000   Max.   :2.000   Max.   :2.000  
                                                                                                                                 
   EK4.CanAU     EK4.CanAU.Diff   SE1.Fam.eh_1   SE1.Fam.oot_1   SE1.Fam.sorry_1 SE1.Fam.washroom_1 SE1.Fam.nasal_1 SE2.Freq.Recent_1
 Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000      Min.   :1.000   Min.   :1.0      
 1st Qu.:1.000   1st Qu.:1.000   1st Qu.:6.000   1st Qu.:5.000   1st Qu.:2.250   1st Qu.:1.000      1st Qu.:1.000   1st Qu.:3.0      
 Median :1.000   Median :1.000   Median :7.000   Median :7.000   Median :6.000   Median :3.000      Median :3.000   Median :4.0      
 Mean   :1.089   Mean   :1.233   Mean   :6.089   Mean   :5.889   Mean   :4.822   Mean   :3.356      Mean   :3.156   Mean   :4.1      
 3rd Qu.:1.000   3rd Qu.:1.000   3rd Qu.:7.000   3rd Qu.:7.000   3rd Qu.:7.000   3rd Qu.:5.000      3rd Qu.:4.750   3rd Qu.:6.0      
 Max.   :2.000   Max.   :2.000   Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.000      Max.   :7.000   Max.   :7.0      
                                                                                                                                     
 SE2.Freq.Child_1 SE2.Freq.Overall_1  SE3.Accuracy   SE4.CanHearImitate SE4.auHearImitate SE5.CanImitate  SE5.auImitate  PE1.KnowCan   
 Min.   :1.000    Min.   :1.000      Min.   :1.000   Min.   :1.000      Min.   :1.000     Min.   :1.000   Min.   :1.0   Min.   :1.000  
 1st Qu.:2.000    1st Qu.:3.000      1st Qu.:5.000   1st Qu.:2.000      1st Qu.:3.000     1st Qu.:1.000   1st Qu.:1.0   1st Qu.:1.000  
 Median :4.000    Median :4.000      Median :5.000   Median :3.000      Median :6.000     Median :2.000   Median :3.0   Median :1.000  
 Mean   :3.678    Mean   :4.278      Mean   :4.944   Mean   :3.233      Mean   :4.833     Mean   :2.244   Mean   :3.5   Mean   :1.378  
 3rd Qu.:5.000    3rd Qu.:5.000      3rd Qu.:6.000   3rd Qu.:4.000      3rd Qu.:6.000     3rd Qu.:3.000   3rd Qu.:6.0   3rd Qu.:2.000  
 Max.   :7.000    Max.   :7.000      Max.   :7.000   Max.   :6.000      Max.   :7.000     Max.   :6.000   Max.   :7.0   Max.   :2.000  
                                                                                                                                       
 PE1.Relatives   PE1.CloseFamFriends PE2.CanSpeakFreq.Recent_1 PE2.CanSpeakFreq.Child_1 PE2.CanSpeakFreq.Overall_1 ME2.HockeyFreq
 Min.   :1.000   Min.   :1.000       Min.   :1.000             Min.   :1.000            Min.   :1.000              Min.   :1.0   
 1st Qu.:2.000   1st Qu.:1.000       1st Qu.:2.000             1st Qu.:1.000            1st Qu.:2.000              1st Qu.:1.0   
 Median :2.000   Median :2.000       Median :3.000             Median :2.000            Median :3.000              Median :2.0   
 Mean   :1.933   Mean   :1.689       Mean   :3.233             Mean   :2.456            Mean   :3.111              Mean   :2.5   
 3rd Qu.:2.000   3rd Qu.:2.000       3rd Qu.:4.000             3rd Qu.:3.000            3rd Qu.:4.000              3rd Qu.:4.0   
 Max.   :2.000   Max.   :2.000       Max.   :7.000             Max.   :7.000            Max.   :7.000              Max.   :7.0   
                                                                                                                                 
 ME3.Sources.People_1 ME3.Sources.TV_1 ME3.Sources.News_1 ME3.Sources.Hockey_1 ME3.Sources.Online_1 ME3.Sources.OtherMedia_1
 Min.   :1.00         Min.   :1.000    Min.   :1.000      Min.   :1.000        Min.   :1.000        Min.   :1.000           
 1st Qu.:1.00         1st Qu.:2.000    1st Qu.:1.000      1st Qu.:1.000        1st Qu.:2.000        1st Qu.:1.000           
 Median :2.00         Median :3.000    Median :2.000      Median :2.000        Median :3.000        Median :1.000           
 Mean   :2.50         Mean   :2.889    Mean   :2.256      Mean   :2.444        Mean   :3.067        Mean   :1.122           
 3rd Qu.:3.75         3rd Qu.:4.000    3rd Qu.:3.000      3rd Qu.:3.750        3rd Qu.:4.000        3rd Qu.:1.000           
 Max.   :7.00         Max.   :7.000    Max.   :7.000      Max.   :7.000        Max.   :7.000        Max.   :5.000           
                                                                                                                            
 ME3.Sources.Other_1 ME4.CanHearFreq.Recent_1 ME4.CanHearFreq.Child_1 ME4.CanHearFreq.Overall_1  tonesCorrect      subject     
 Min.   :1.000       Min.   :1.000            Min.   :1.0             Min.   :1.000             Min.   :0.000   Min.   : 1.00  
 1st Qu.:1.000       1st Qu.:2.250            1st Qu.:2.0             1st Qu.:2.000             1st Qu.:5.000   1st Qu.:23.25  
 Median :1.000       Median :3.000            Median :2.5             Median :3.000             Median :6.000   Median :45.50  
 Mean   :1.144       Mean   :3.622            Mean   :2.8             Mean   :3.322             Mean   :5.522   Mean   :45.50  
 3rd Qu.:1.000       3rd Qu.:5.000            3rd Qu.:4.0             3rd Qu.:4.000             3rd Qu.:6.000   3rd Qu.:67.75  
 Max.   :5.000       Max.   :7.000            Max.   :7.0             Max.   :7.000             Max.   :6.000   Max.   :90.00  
                                                                                                                               
quesdata.final

# Write to file
write.csv(quesdata.final, 'data/axb_1b_lbq_data.csv', row.names=F)

Summarize Data

Tables

All Scores

# Total
quesdata.final %>% select(participantId, conditionId, guiseCombination, Age, Gender, Ethnicity, CEscore, SAscore, MSscore, EQscore, EQ.raws) %>%
  summarise_if(is.numeric, list(mean = mean, sd = sd, min = min, max = max)) %>% 
  pivot_longer(everything(), names_to = c("Measure", "Stat"), names_sep = "_", values_to = "value") %>%
  pivot_wider(Measure, names_from = "Stat", values_from = "value") %>%
  mutate(mean = round(mean, digits = 6)) %>%
  mutate(group = "Total") %>%
  select(group, everything())

?summarise_if
# Baseline
quesdata.final %>% select(participantId, conditionId, guiseCombination, Age, Gender, Ethnicity, CEscore, SAscore, MSscore, EQscore, EQ.raws) %>%
  filter(guiseCombination == "baseline") %>%
  summarise_if(is.numeric, list(mean = mean, sd = sd, min = min, max = max)) %>% 
  pivot_longer(everything(), names_to = c("Measure", "Stat"), names_sep = "_", values_to = "value") %>%
  pivot_wider(Measure, names_from = "Stat", values_from = "value") %>%
  mutate(mean = round(mean, digits = 6)) %>%
  mutate(group = "baseline") %>%
  select(group, everything())

# Match
quesdata.final %>% select(participantId, conditionId, guiseCombination, Age, Gender, Ethnicity, CEscore, SAscore, MSscore, EQscore, EQ.raws) %>%
  filter(guiseCombination == "match") %>%
  summarise_if(is.numeric, list(mean = mean, sd = sd, min = min, max = max)) %>% 
  pivot_longer(everything(), names_to = c("Measure", "Stat"), names_sep = "_", values_to = "value") %>%
  pivot_wider(Measure, names_from = "Stat", values_from = "value") %>%
  mutate(mean = round(mean, digits = 6)) %>%
  mutate(group = "match") %>%
  select(group, everything())

# Mismatch
quesdata.final %>% select(participantId, conditionId, guiseCombination, Age, Gender, Ethnicity, CEscore, SAscore, MSscore, EQscore, EQ.raws) %>%
  filter(guiseCombination == "mismatch") %>%
  summarise_if(is.numeric, list(mean = mean, sd = sd, min = min, max = max)) %>% 
  pivot_longer(everything(), names_to = c("Measure", "Stat"), names_sep = "_", values_to = "value") %>%
  pivot_wider(Measure, names_from = "Stat", values_from = "value") %>%
  mutate(mean = round(mean, digits = 6)) %>%
  mutate(group = "mismatch") %>%
  select(group, everything())

PCA Contributors

#quesdata.final.sum <- 
  quesdata.final %>% group_by(guiseCombination) %>%
  summarize(#meanEQ = mean(EQscore), 
            meanIK2.au = mean(IK2.au), meanIK2.ai = mean(IK2.ai), 
            #meanDecideCan = mean(IK1.DecideCanada), 
            #meanEhFam = mean(SE1.Fam.eh_1), 
            meanOotFam = mean(SE1.Fam.oot_1), 
            meanSEFreq = mean(SE2.Freq.Overall_1), meanSEFreq.C = mean(SE2.Freq.Child_1), 
            meanSEFreq.R = mean(SE2.Freq.Recent_1), 
            meanSEAcc = mean(SE3.Accuracy))

#quesdata.final.sum <- 
  quesdata.final %>% group_by(guiseCombination) %>%
  summarize(CanHearFreq = mean(ME4.CanHearFreq.Overall_1), 
            CanHearFreq.R = mean(ME4.CanHearFreq.Recent_1),
            CanHearFreq.C = mean(ME4.CanHearFreq.Child_1),
            CanSpeakFreq = mean(PE2.CanSpeakFreq.Overall_1),
            CanSpeakFreq.R = mean(PE2.CanSpeakFreq.Recent_1),
            CanSpeakFreq.C = mean(PE2.CanSpeakFreq.Child_1))

Yes/No Responses

quesdata.final %>% group_by(guiseCombination) %>% mutate(EK1.CanSpeak = ifelse(EK1.CanSpeak==1, "yes", "no")) %>%
count(EK1.CanSpeak) %>%
  pivot_wider(names_from = EK1.CanSpeak, values_from = n) %>%
  mutate(question="SpeakDiff") %>% relocate(question)

quesdata.final %>% group_by(guiseCombination) %>% mutate(EK2.CanPron = ifelse(EK2.CanPron==1, "yes", "no")) %>%
count(EK2.CanPron) %>%
  pivot_wider(names_from = EK2.CanPron, values_from = n)  %>%
  mutate(question="PronounceDiff") %>% relocate(question)

quesdata.final %>% group_by(guiseCombination) %>% mutate(EK3.CanAI = ifelse(EK3.CanAI==1, "yes", "no")) %>%
count(EK3.CanAI) %>%
  pivot_wider(names_from = EK3.CanAI, values_from = n) %>%
  mutate(prop.yes = yes/(yes+no))  %>%
  mutate(question="aiDiff") %>% relocate(question)

quesdata.final %>% group_by(guiseCombination) %>% mutate(EK4.CanAU = ifelse(EK4.CanAU==1, "yes", "no")) %>%
count(EK4.CanAU) %>%
  pivot_wider(names_from = EK4.CanAU, values_from = n)  %>%
  mutate(prop.yes = yes/(yes+no))  %>%
  mutate(question="auDiff") %>% relocate(question)
# Overall total
quesdata.final %>% mutate(EK3.CanAI = ifelse(EK3.CanAI==1, "yes", "no")) %>% count(EK3.CanAI) %>%
  pivot_wider(names_from = EK3.CanAI, values_from = n) %>% mutate(prop.yes = yes/(yes+no))  %>%
  mutate(question="aiDiff") %>% relocate(question) %>%
  rbind(.,quesdata.final %>% mutate(EK4.CanAU = ifelse(EK4.CanAU==1, "yes", "no")) %>% count(EK4.CanAU) %>%
          pivot_wider(names_from = EK4.CanAU, values_from = n)%>% mutate(prop.yes = yes/(yes+no))  %>%
          mutate(question="auDiff") %>% relocate(question)
  )
NA

Plots

EQ Scores

# By Gender
ggplot(data=quesdata.final, aes(x=Gender, y=EQscore)) +
  geom_boxplot(aes(linetype = Gender), fill="grey", alpha=0.3, na.rm=TRUE) +
  gg_theme()


# By Gender + Guise
ggplot(data=quesdata.final, aes(x=Gender, y=EQscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination, linetype=Gender), alpha=0.3, na.rm=TRUE) +
  facet_grid(~guiseCombination) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()


# By Guise
ggplot(data=quesdata.final, aes(x=guiseCombination, y=EQscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination), alpha=0.3, na.rm=TRUE) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()

SA Scores

# Density Plot of score distributions
quesdata.final %>% mutate(guiseCombination = plyr::mapvalues(guiseCombination, from = c("baseline", "match", "mismatch"), to = c("No-Guise", "Guise-Match", "Guise-Mismatch"))) %>%
  ggplot(aes(x=SAscore,fill=guiseCombination,color=guiseCombination))+
   geom_density(alpha=0.3) +
  labs(title="", x="SA Score", y="Count") +
  gg_theme()

# By Gender
ggplot(data=quesdata.final, aes(x=Gender, y=SAscore)) +
  geom_boxplot(aes(linetype = Gender), fill="grey", alpha=0.3, na.rm=TRUE) +
  gg_theme()


# By Gender + Guise
ggplot(data=quesdata.final, aes(x=Gender, y=SAscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination, linetype=Gender), alpha=0.3, na.rm=TRUE) +
  facet_grid(~guiseCombination) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()


# By Guise
ggplot(data=quesdata.final, aes(x=guiseCombination, y=SAscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination), alpha=0.3, na.rm=TRUE) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()

CE Scores

# Density Plot of score distributions
There were 16 warnings (use warnings() to see them)
quesdata.final %>% mutate(guiseCombination = plyr::mapvalues(guiseCombination, from = c("baseline", "match", "mismatch"), to = c("No-Guise", "Guise-Match", "Guise-Mismatch"))) %>%
  ggplot(aes(x=CEscore,fill=guiseCombination,color=guiseCombination))+
   geom_density(alpha=0.3) +
  labs(title="", x="CE Score", y="Count") +
  gg_theme()

# By Gender
ggplot(data=quesdata.final, aes(x=Gender, y=CEscore)) +
  geom_boxplot(aes(linetype = Gender), fill="grey", alpha=0.3, na.rm=TRUE) +
  gg_theme()


# By Gender + Guise
ggplot(data=quesdata.final, aes(x=Gender, y=CEscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination, linetype=Gender), alpha=0.3, na.rm=TRUE) +
  facet_grid(~guiseCombination) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()


# By Guise
ggplot(data=quesdata.final, aes(x=guiseCombination, y=CEscore)) +
  geom_boxplot(aes(fill = guiseCombination, color=guiseCombination), alpha=0.3, na.rm=TRUE) +
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  scale_fill_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()

PCA Output Scores


CSCE.biplot <- quesdata.final %>% mutate(guiseCombination = plyr::mapvalues(guiseCombination, from = c("baseline", "match", "mismatch"), to = c("No-Guise", "Guise-Match", "Guise-Mismatch"))) %>%
ggplot(aes(y=CEscore, x=SAscore, color=guiseCombination, shape=guiseCombination)) + 
  geom_point(na.rm=TRUE, size=3, alpha=0.7) +
  #geom_text(aes(label=subject),hjust=1.5,vjust=1.5, color="#00AFBB", check_overlap=T)+

  scale_x_continuous(lim=c(-2.5, 2.5),breaks=seq(-3,3,1)) +
  scale_y_continuous(lim=c(-3.5, 3.5),breaks=seq(-3,3,1)) +
  geom_hline(yintercept=0, linetype="dashed") +
  geom_vline(xintercept=0, linetype="dashed") +
  labs(y="CE Scores (PC1)", x="SA Scores (PC2)", color="Participant Group", shape="Participant Group") +
  
  scale_color_manual(values=ghibli_palette("PonyoMedium")[c(6,3,4)]) +
  gg_theme()
CSCE.biplot


#ggsave(path="plots", filename="CS-CE_distribution_plot.png", CSCE.biplot, width=16, height=8, units = "in" , dpi=72)

Correlations

quesdata.final %>% 
  ggplot(aes(x = SAscore, y = EQscore)) + 
  geom_point(stat="identity", aes(colour = factor(Gender)), cex=2) +
  geom_smooth(method="lm") +

  labs(y = "EQ Score", x = "SA Score", color="Gender",
       title="Correlations: By Speaker") +
  scale_color_manual(values=ghibli_palette("PonyoLight")[c(4,3,2)])+
  gg_theme()

quesdata.final %>% 
  ggplot(aes(x = SAscore, y = CEscore)) + 
  geom_point(stat="identity", aes(colour = factor(Gender)), cex=2) +
  geom_smooth(method="lm") +

  labs(y = "CE Score", x = "SA Score", color="Gender",
       title="Correlations: By Speaker") +
  scale_color_manual(values=ghibli_palette("PonyoLight")[c(4,3,2)])+
  gg_theme()

quesdata.final %>% 
  ggplot(aes(x = CEscore, y = EQscore)) + 
  geom_point(stat="identity", aes(colour = factor(Gender)), cex=2) +
  geom_smooth(method="lm") +

  labs(y = "EQ Score", x = "CE Score", color="Gender",
       title="Correlations: By Speaker") +
  scale_color_manual(values=ghibli_palette("PonyoLight")[c(4,3,2)])+
  gg_theme()

LS0tCnRpdGxlOiAiTUlDUiBBWEIgRXhwZXJpbWVudCAxYiAoQVhCLXAyKSBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBTZXQgdXAKIyMgUGFja2FnZXMKYGBge3IsIHdhcm5pbmc9Rn0KIyBXcmFuZ2xpbmcKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWdzdWIpCgojIFN0YXRpc3RpY3MvTnVtZXJpY2FsIHByb2Nlc3NpbmcKbGlicmFyeShicm1zKQoKIyBQQ0EKbGlicmFyeShGYWN0b01pbmVSKSAKbGlicmFyeShmYWN0b2V4dHJhKSAKbGlicmFyeShHUEFyb3RhdGlvbikKbGlicmFyeShwYXJhbikKCiMgUGxvdHRpbmcKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdoaWJsaSkKCiMgT3B0aW9uYWwgc2V0dGluZ3MKb3B0aW9ucyhkcGx5ci5zdW1tYXJpc2UuaW5mb3JtPUYpICMgU3RvcCBkcGx5ciBmcm9tIHByaW50aW5nIHN1bW1hcmlzZSBlcnJvciAodGhhdCBpc24ndCBhbiBlcnJvcikKc2VsZWN0IDwtIGRwbHlyOjpzZWxlY3QgIyBFbnN1cmUgdGhhdCBzZWxlY3QoKSBjb21tYW5kIGlzIHRoZSBkcGx5ciBjb21tYW5kIChjbGFzaGVzIHdpdGggTUFTUywgd2hpY2ggaXMgaW1wb3J0ZWQvcmVxdWlyZWQgYnkgcGFyYW4pCmBgYAoKIyMgRnVuY3Rpb25zCmBgYHtyLCB3YXJuaW5nPUZ9CiMgU3RhbmRhcmQgZXJyb3IgZnVuY3Rpb24Kc3RkLmVycm9yIDwtIGZ1bmN0aW9uKHgsIG5hLnJtID0gVCkgewogIHNxcnQodmFyKHgsIG5hLnJtID0gbmEucm0pL2xlbmd0aCh4W2NvbXBsZXRlLmNhc2VzKHgpXSkpCn0KCiMgZ2dwbG90IHRoZW1lCmdnX3RoZW1lIDwtIGZ1bmN0aW9uKCkgewogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MjUpLAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTUsIGZhY2U9Iml0YWxpYyIpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNSksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9ZWxlbWVudF9yZWN0KGZpbGw9IndoaXRlIiksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE1KSkrCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTUsIGZhY2U9ImJvbGQiKSwKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNSkpCn0KYGBgCgojIC4uLgojIFBlcmNlcHRpb24gRGF0YQojIyBQcmUtUHJvY2VzcyBEYXRhCiMjIyBSZWFkICYgQ2xlYW4gUmVzdWx0cwoKUmVhZCBpbiB0aGUgZGF0YSBmcm9tIGRvd25sb2FkZWQgQ1NWIGZpbGVzIGZyb20gRmlyZWJhc2UuIEFsc28gcmVtb3ZlIGRhdGEgZnJvbSBzdWJqZWN0cyB3aG8gZGlkIG5vdCBjb21wbGV0ZSB0aGUgc3R1ZHkgKCJyZXR1cm5lZCIgb24gUHJvbGlmaWMpIG9yIGhhdmUgYmVlbiBpZGVudGlmaWVkIHRvIGJlIG91dGxpZXJzLCBub3QgZm9sbG93aW5nIGluc3RydWN0aW9ucywgbm90IGZ1bGZpbGxpbmcgbXkgcGFydGljaXBhbnQgcmVxdWlyZW1lbnRzLCBldGMuIE91dGxpZXJzIGFyZSBkZW50aWZpZWQgaW4gYSBsYXRlciBzZWN0aW9uIGJlbG93IChPdXRsaWVyIENoZWNrKSwgd2hpbGUgcGFydGljaXBhbnQgcmVxdWlyZW1lbnRzIGFyZSBjaGVja2VkIGluIHRoZSBkYXRhIGZyb20gdGhlIHF1ZXN0aW9ubmFpcmUuCgpgYGB7cn0KIyBMaXN0IHJlc3VsdHMgZmlsZXMgcGVyIHN1YmplY3QKZmlsZWxpc3QgPC0gbGlzdC5maWxlcyhwYXRoPSIuL2RhdGEvYXhiXzFiL3BlcmNlcHRpb24vIiwgcGF0dGVybj0iLmNzdiIsZnVsbC5uYW1lcz1UUlVFKSAKCiMgQ2hlY2sgTnVtYmVyIG9mIGZpbGVzCnBhc3RlKCJUb3RhbCBudW1iZXIgb2YgcGVyY2VwdGlvbiByZXN1bHRzIGZpbGVzIGluIGRpcmVjdG9yeToiLGxlbmd0aChmaWxlbGlzdCkpIApwYXN0ZSgiRXhwZWN0ZWQgbnVtYmVyIG9mIHF1ZXN0aW9ubmFpcmUgZmlsZXM6IixsZW5ndGgoZmlsZWxpc3QpLTItMikgIyAtMiAocmVwZWF0cykgLTIgKHJldHVybnMpIApgYGAKCkluIHRoaXMgY2FzZSwgd2hlbiByZWFkaW5nIGluIHRoZSBkYXRhIGJ5IHBhcnRpY2lwYW50IGZpbGUsIHRoZSBleHBlcmltZW50IHN0YXJ0IHRpbWUgaXMgZXh0cmFjdGVkIGZyb20gdGhlIHN0cnVjdHVyZSBgLi9kYXRhL2F4Yl9YWC9wZXJjZXB0aW9uL3N1YmpudW1iZXJfc3RhcnR0aW1lLmNzdmAgd2hlcmUgSSBmaXJzdCByZW1vdmUgdGhlICIuY3N2IiB3aXRoIGdzdWIsIHNwbGl0IHRoZSBzdHJpbmcgaW50byB0aHJlZSBiYXNlZCBvbiAiXyIsIHRoZW4gc2VsZWN0IHRoZSB0aGlyZCBpdGVtIGZyb20gdGhlIGZpcnN0IG91dHB1dCBvYmplY3QgKGBbWzFdXVszXWApLgoKYGBge3J9CiMgUmVhZCBhbmQgQ29uY2F0ZW5hdGUgcmVzdWx0cwojIyAoMSkgSnVzdCByZWFkIGFuZCBjb25jYXQKIyBjb25kYXRhLnJlYWQgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGZpbGVsaXN0LCByZWFkLmNzdikpCgojIyAoMikgUmVhZCwgY29uY2F0IGFuZCBleHRyYWN0IHBhcnQgb2YgZmlsZW5hbWUKY29uZGF0YS5yZWFkIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShmaWxlbGlzdCwgZnVuY3Rpb24oeCkgY2JpbmQocmVhZC5jc3YoeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnR0aW1lPXN0cnNwbGl0KGdzdWIoIi5jc3YiLCIiLHgpLCAiXyIpW1sxXV1bM10pKSkKIyMgVE9ETzogVXBkYXRlIHdpdGggc3ViamVjdCBudW1iZXJzIGFzIG5lY2Vzc2FyeQpjb25kYXRhLnJlYWQgPC0gY29uZGF0YS5yZWFkICU+JSAKICAKICAjIEZpeCBkYXRhIHdpdGggdW5kZWZpbmVkIHN1YmplY3QKICBtdXRhdGUocGFydGljaXBhbnRJZCA9IHJlcGxhY2VfbmEocGFydGljaXBhbnRJZCwgJzVkMWUyMDQ1YTM3YTRkMDAxYTFmYzJjYicpKSAlPiUKICAKICAjIFJlbW92ZSBkYXRhIGZyb20gZHJvcHBlZCBzdWJqZWN0cwogIHN1YnNldChwYXJ0aWNpcGFudElkICE9ICc1YTY4ZDFjMWMwZDgzNjAwMDEwODIxZTAnKSAlPiUgICAjIGRpZCBub3QgZmluaXNoIC0tIFJFVFVSTkVECiAgc3Vic2V0KHBhcnRpY2lwYW50SWQgIT0gJzVmY2QwZWU0MDZiZDdhYjk0ZWNjNDI0YycpICU+JSAgICMgc3Bva2FuZSArIDUwJSB0b28gZmFzdCAtLSBSRVRVUk5FRAogIHN1YnNldChwYXJ0aWNpcGFudElkICE9ICc1ZTY3ZjAzMjFlNGYwYTBhNjU3YzFkMDgnKSAlPiUgICAjIG5vdCBtaWNoaWdhbiAtIGZsb3JpZGEgdG8gMjIKICBzdWJzZXQocGFydGljaXBhbnRJZCAhPSAnNWY5Zjk2YzdlYTdmOTY1ZjkyZjVkNDg4JykgJT4lICAgIyBub3QgbWljaGlnYW4gLSBjb2xvcmFkbyB0byAxOAogIHN1YnNldChwYXJ0aWNpcGFudElkICE9ICc1ZmZhMDFlYjdkZmI0MzM4N2E4NjgxOTYnKSAlPiUgICAjIG5vdCBtaWNoaWdhbiAtIG55IHRvIDI4CiAgc3Vic2V0KHBhcnRpY2lwYW50SWQgIT0gJzVmZWU4Y2YzOGFjMTgyMTQ0NjBjNTllZCcpICU+JSAgICMgbm90IG1pY2hpZ2FuIC0gcGEgdG8gMTUKICBzdWJzZXQocGFydGljaXBhbnRJZCAhPSAnNTY5OWQyNTYyNWQ5ZTkwMDBkYjBjN2NjJykgJT4lICAgIyAxNjggKyA4NSBndXkuLi4gZGlkIHR3aWNlKD8pIGFuZCBza2lwcGVkIGFsbAogIHN1YnNldChwYXJ0aWNpcGFudElkICE9ICc2MDFmMDRmYjIzNWNiMTQwMjBjN2E5NmYnKSAlPiUgICAjIGRpZCB0d2ljZSwgb25jZSB3aXRoIGNvbmRBIHRoZW4gY29uZEMsIHNvIGNhbid0IHVzZSBldmVuIGZ1bGwgZGF0YQogIHN1YnNldChwYXJ0aWNpcGFudElkICE9ICc1ZmUwZTc4OTI3MzhhZmE2YTIxY2ZkN2MnKSAlPiUgICAjIGRpZCBub3QgZmluaXNoIHN0dWR5L3F1ZXN0aW9ubmFpcmUgLS0gUkVUVVJORUQKIyBzdWJzZXQocGFydGljaXBhbnRJZCAhPSAnNWZjNTExYzMyYjliYzYwYmMxODliODkzJykgJT4lICAgIyBhbGllbiBndXkgLS0tIEtFUFQgaW4gdGhlIGVuZAoKICAjIFJlbW92ZSBsZXZlbHMgb2YgZHJvcHBlZCBzdWJqZWN0cwogIGRyb3BsZXZlbHMoKQoKYGBgCgpgYGB7cn0KIyBUcmltIHVubmVjZXNzc2FyeSBjb2x1bW5zIGFuZCByb3dzCmNvbmRhdGEgPC0gc3Vic2V0KGNvbmRhdGEucmVhZCwsLWModXJsLCBpbnRlcm5hbF9ub2RlX2lkLCB2aWV3X2hpc3RvcnksIHN0aW11bHVzLCBzdWNjZXNzLCBrZXlfcHJlc3MsIHRyaWFsX2luZGV4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJpYWxfdHlwZSwgd29yZFN0aW0pKQoKIyBLZWVwIG9ubHkgdG9uZXRlc3QgYW5kIHRlc3Qgcm93cwojIENyZWF0ZSBzdWJqZWN0IG51bWJlciBjb2x1bW4gYnkgb3JkZXIgb2YgcGFydGljaXBhdGlvbiAoZmlyc3Qgc29ydGluZyBzdWJqZWN0cyBieSBzdGFydHRpbWUsIHRoZW4gYWRkaW5nIHN1YmpudW0gY29sdW1uKQojIFJlY292ZXIgdm93ZWwgZGF0YSBmcm9tIHNlbnROdW0gY29sdW1uICg9PSBWb3dlbCBkYXRhIHdhcyBhY2NpZGVudGx5IGxlZnQgb3V0IG9mIHN0aW11bGkgZGF0YSkKIyBNYW5pcHVsYXRlIGRhdGEgdHlwZXMKIyBSZW9yZGVyIGNvbHVtbnMKY29uZGF0YSA8LSBjb25kYXRhICU+JQogIHN1YnNldCh0cmlhbF9yb2xlID09ICJ0ZXN0IiB8IHRyaWFsX3JvbGUgPT0gInRvbmV0ZXN0IikgJT4lCiAgCiAgI2FycmFuZ2UoZGVzYyhzdGFydHRpbWUpKSAlPiUKICAjbXV0YXRlKHN1YmpOdW0gPSBhcy5mYWN0b3IocmVwKDE6blN1YmosIGVhY2g9KG5yb3coLikvblN1YmopKSkpICU+JQogIAogIG11dGF0ZSh2b3dlbCA9IGNhc2Vfd2hlbihiZXR3ZWVuKHNlbnROdW0sIDExLCAyMCkgfiAiQVUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBiZXR3ZWVuKHNlbnROdW0sIDIxLCAzMCkgfiAiQUkiKSkgJT4lCiAgbXV0YXRlKGd1aXNlQ29tYmluYXRpb24gPSBjYXNlX3doZW4oY29uZGl0aW9uSWQgPT0gImNvbmRBIiB8IGNvbmRpdGlvbklkID09ICJjb25kQiIgfiAibWF0Y2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uSWQgPT0gImNvbmRDIiB8IGNvbmRpdGlvbklkID09ICJjb25kRCIgfiAibWlzbWF0Y2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uSWQgPT0gImNvbmRFIiB8IGNvbmRpdGlvbklkID09ICJjb25kRiIgfiAiYmFzZWxpbmUiKSkgJT4lCiAgbXV0YXRlKHNwZWFrZXJPcmRlciA9IGNhc2Vfd2hlbihjb25kaXRpb25JZCA9PSAiY29uZEEiIHwgY29uZGl0aW9uSWQgPT0gImNvbmRDIiB8IGNvbmRpdGlvbklkID09ICJjb25kRSIgfiAiUzMtUzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uSWQgPT0gImNvbmRCIiB8IGNvbmRpdGlvbklkID09ICJjb25kRCIgfCBjb25kaXRpb25JZCA9PSAiY29uZEYiIH4gIlM5LVMzIikpICU+JQogIAogIG11dGF0ZShydD1hcy5udW1lcmljKHJ0KSkgJT4lCiAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMuZmFjdG9yKSAlPiUKICBtdXRhdGUodGltZV9lbGFwc2VkX3NlYyA9IHRpbWVfZWxhcHNlZC8xMDAwLCBydF9zZWMgPSBydC8xMDAwKSAlPiUKICAKICBzZWxlY3QocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQsIHRyaWFsX3JvbGUsIHRpbWVfZWxhcHNlZCwgdGltZV9lbGFwc2VkX3NlYywgcnQsIHJ0X3NlYywgZXZlcnl0aGluZygpKQoKIyBDaGVjayBkYXRhCmNvbmRhdGEKI3N1bW1hcnkoY29uZGF0YSkKYGBgCgojIyMgQ2hlY2sgUmVzdWx0cwojIyMjIENoZWNrIFBhcnRpY2lwYW50cwpgYGB7cn0KIyBDaGVjayBudW1iZXIgb2YgZGF0YSBwb2ludHMgcGVyIHN1YmplY3QKIyBDb3JyZWN0IG51bWJlciBvZiBkYXRhIHBvaW50cyBpcyAxNzQvMTgwLzE4NiA9ICgxNjggKyA2LzEyLzE4KSAKKG5EYXRhLmJ5c3ViaiA8LSBjb25kYXRhICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbklkLCBwYXJ0aWNpcGFudElkLCBzdGFydHRpbWUpICU+JQogIGNvdW50KCkpCmBgYAoKYGBge3J9CiMgQ2hlY2sgbnVtYmVyIG9mIGRhdGEgcG9pbnRzIHBlciBjb25kaXRpb24gKGdvYWw6IDQwIHBlciBjb25kaXRpb24pCihuU3Viai5ieWNvbmQgPC0gbkRhdGEuYnlzdWJqICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbklkKSAlPiUKICBjb3VudCgpKQpgYGAKClRyb3VibGVzaG9vdCBwYXJ0aWNpcGFudCBpc3N1ZXMgbWFudWFsbHkgYW5kL29yIGJ5IGZpbHRlcmluZyBkdXBsaWNhdGUgc3ViamVjdCBmaWxlcyBvciB0aG9zZSB3aXRoIHRvbyBmZXcgcmVzcG9uc2VzLgoKYGBge3J9CiMgTWFudWFsbHkgc2Nyb2xsIHRvIGNoZWNrIGRhdGEgYXMgbmVlZGVkClZpZXcobkRhdGEuYnlzdWJqKQpgYGAKCmBgYHtyfQojIFRyb3VibGVzaG9vdCBkdXBsaWNhdGVkIHBhcnRpY2lwYW50cwojIyBDYWxjdWxhdGUgbnVtYmVyIG9mIHN1YmplY3RzIHZzIGRhdGEgZmlsZXMKCnBhc3RlKCJUb3RhbCBudW1iZXIgb2Ygc2NyZWVuZWQgcGVyY2VwdGlvbiBkYXRhIGZpbGVzOiIsbnJvdyhuRGF0YS5ieXN1YmopKSAjIG51bWJlciBvZiBkYXRhIGZpbGVzCnBhc3RlKCJUb3RhbCBudW1iZXIgb2YgdW5pcXVlIHBhcnRpY2lwYW50IG51bWJlcnM6IixsZW5ndGgodW5pcXVlKGNvbmRhdGEucmVhZCRwYXJ0aWNpcGFudElkKSkpICMgdW5pcXVlIHN1YmogbnVtcwpgYGAKCgpgYGB7cn0KIyMgSWRlbnRpZnkgZHVwcwpuRGF0YS5ieXN1YmokZHVwcyA9IGR1cGxpY2F0ZWQobkRhdGEuYnlzdWJqJHBhcnRpY2lwYW50SWQpCm5EYXRhLmJ5c3ViaiAlPiUgZmlsdGVyKGR1cHM9PVRSVUUpCgojIFRyb3VibGVzaG9vdCBoYWxmIGRhdGEKbkRhdGEuYnlzdWJqICU+JSBmaWx0ZXIobjwxNzQpCgpgYGAKCiMjIyMgQ2hlY2sgVG9uZSBUZXN0CmBgYHtyfQojIHNlbGVjdCAiVG9uZXRlc3QiIGRhdGEKIyBDaGVjayB3aGljaCBwYXJ0aWNpcGFudHMgZ290IDQvNiBvciBiZWxvdyBvbiB0aGUgaGVhZHBob25lL2F0dGVudGlvbiBjaGVjawpjb25kYXRhLnRvbmVzIDwtIGNvbmRhdGEgJT4lCiAgc3Vic2V0KHRyaWFsX3JvbGUgPT0gInRvbmV0ZXN0IikgJT4lCiAgbXV0YXRlKGNvcnJlY3RfcmVzcG9uc2U9dG9sb3dlcihjb3JyZWN0X3Jlc3BvbnNlKSkgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQpICU+JQogIHNsaWNlX21heChvcmRlcl9ieSA9IHRpbWVfZWxhcHNlZCwgbiA9IDYpICMgZ2V0IGxhc3QgNiB0b25lIHRyaWFscywgYnkgbGFyZ2VzdCB0aW1lX2VsYXBzZWQKCmNvbmRhdGEudG9uZXMgPC0gY29uZGF0YS50b25lcyAlPiUKICBncm91cF9ieShwYXJ0aWNpcGFudElkLCBjb25kaXRpb25JZCkgJT4lCiAgc3VtbWFyaXNlKHRvbmVzQ29ycmVjdCA9IHN1bShjb3JyZWN0X3Jlc3BvbnNlPT0idHJ1ZSIpKSAlPiUKICB1bmdyb3VwKCkKY29uZGF0YS50b25lcwpgYGAKCmBgYHtyfQojIFRyb3VibGVzaG9vdCBoYWxmIGRhdGEKY29uZGF0YS50b25lcyAlPiUgZmlsdGVyKHRvbmVzQ29ycmVjdDw1KQpgYGAKCiMjIyMgQ2hlY2sgQVhCIFRyaWFscwoKYGBge3J9CiMgTG9hZCBzdGltLmR1cmF0aW9ucy5maW5hbCBkYXRhZnJhbWUKbG9hZChmaWxlPSIuL2RhdGEvc3RpbV9kdXJhdGlvbnMuckRhdGEiKQoKIyBTZWxlY3QgIlRlc3QiIGRhdGEgKyByZW1vdmUgY29sdW1ucyBmb3IgVG9uZXRlc3QgZGF0YQojIE1lcmdlIHRvbmVzQ29ycmVjdCBjb2x1bW4gaW4gZm9yIGxhdGVyIGRlY2lzaW9ucyAoZS5nLiBpZiByZW1vdmUgcGVvcGxlIHdpdGggbG93IHNjb3JlIGluIHRvbmUgdGVzdCkKCmNvbmRhdGEudGVzdCA8LSBjb25kYXRhICU+JQogIHN1YnNldCh0cmlhbF9yb2xlID09ICJ0ZXN0IikgJT4lCiAgc3Vic2V0KC4sLC1jKGJ1dHRvbl9wcmVzc2VkLCBjb3JyZWN0X2Fuc3dlciwgY29ycmVjdF9yZXNwb25zZSkpICU+JQogIGRyb3BsZXZlbHMoKSAlPiUKICAKICBtZXJnZSguLCBjb25kYXRhLnRvbmVzLCBhbGwueD1UUlVFKSAlPiUKICBtZXJnZSguLCBzdGltLmR1cmF0aW9ucy5maW5hbCwgYWxsLng9VFJVRSkgJT4lCiAgCiAgc2VsZWN0KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkLCB0cmlhbF9yb2xlLCB0aW1lX2VsYXBzZWQsIHRpbWVfZWxhcHNlZF9zZWMsIHJ0LCBydF9zZWMsIHJhaXNlZF9yZXNwb25zZSwgZXZlcnl0aGluZygpKQpjb25kYXRhLnRlc3QKYGBgCgpgYGB7cn0KIyBDaGVjayBkYXRhIGZvciBvYnZpb3VzIGlzc3VlcwpzdW1tYXJ5KGNvbmRhdGEudGVzdCkKYGBgCgpgYGB7cn0KIyBDaGVjayBvcmlnaW5hbCBkYXRhIHBvaW50cwooZGF0YXBvaW50cy5vZyA8LSBucm93KGNvbmRhdGEudGVzdCkpCmBgYAoKIyMjIyBDaGVjayBBWEIgT3V0bGllcnMKYGBge3J9CiMgV2hhdCBhcmUgdGhlIGRlc2NyaXB0aXZlIHN0YXRzIG9uIHRvdGFsIGV4cGVyaW1lbnQgdGltZSBhbmQgcmVhY3Rpb24gdGltZXM/CmNvbmRhdGEudGVzdC5ieXN1YmogPC0gY29uZGF0YS50ZXN0ICU+JQogIGdyb3VwX2J5KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkKSAlPiUKICBzdW1tYXJpemUodGltZV9lbGFwc2VkX21pbiA9IG1heCh0aW1lX2VsYXBzZWRfc2VjLzYwKSwgbWVhbl9ydF9zZWMgPSBtZWFuKHJ0X3NlYyksIG1pbl9ydF9zZWMgPSBtaW4ocnRfc2VjKSwgIG1heF9ydF9zZWMgPSBtYXgocnRfc2VjKSwgc2RfcnRfc2VjID0gc2QocnRfc2VjKSkgJT4lCiAgdW5ncm91cCgpCmhlYWQoY29uZGF0YS50ZXN0LmJ5c3ViaikKCiMgU3VtbWFyeSBvZiB0b3RhbCBleHBlcmltZW50IHRpbWVzCiMgQ2hlY2sgZm9yIGVzcGVjaWFsbHkgc2hvcnQgb3IgbG9uZyB0aW1lcwpjb25kYXRhLnRlc3Qub3ZlcmFsbCA8LSBjb25kYXRhLnRlc3QuYnlzdWJqICU+JQogIHN1bW1hcml6ZShtZWRpYW5fdGltZSA9IG1lZGlhbih0aW1lX2VsYXBzZWRfbWluKSwgbWVhbl90aW1lPSBtZWFuKHRpbWVfZWxhcHNlZF9taW4pLCBtaW5fdGltZSA9IG1pbih0aW1lX2VsYXBzZWRfbWluKSwgbWF4X3RpbWUgPSBtYXgodGltZV9lbGFwc2VkX21pbikpCmNvbmRhdGEudGVzdC5vdmVyYWxsCmBgYAoKCmBgYHtyfQojIFJUIE91dGxpZXIgY2hlY2sKIyBDYWxjdWxhdGUgcmVzcG9uc2UgdGltZXMgdGhhdCBhcmUgYXQgbGVhc3QgMyBTRHMgYXdheSBmcm9tIHRoZSBtZWFuCmNvbmRhdGEudGVzdC50aW1lc3VtIDwtIGNvbmRhdGEudGVzdCAlPiUKICBzdW1tYXJpemUobWVhblRpbWUgPSBtZWFuKHJ0KSwgc2RUaW1lID0gc2QocnQpLCBtaW5UaW1lID0gbWluKHJ0KSwgbWF4VGltZSA9IG1heChydCksIG1lZGlhblRpbWUgPSBtZWRpYW4ocnQpLCBpcXJUaW1lID0gSVFSKHJ0KSwgbWVhblN0aW1UaW1lID0gbWVhbihkdXJfbXMpKSAlPiUKICBtdXRhdGUoc2QzID0gc2RUaW1lKjMsIGlxcjMgPSBpcXJUaW1lKjMsIHN0aW1UaW1lMTAgPSBtZWFuU3RpbVRpbWUrMTAwMDApCmNvbmRhdGEudGVzdC50aW1lc3VtCmBgYAoKCmBgYHtyfQojIEFkZCBjb2x1bW5zIG9mIG91dGxpZXIgY3JpdGVyaWEgCmNvbmRhdGEudGVzdC5jaGVjayA8LSBjb25kYXRhLnRlc3QgJT4lCiAgbXV0YXRlKHJ0Lm91dGxpZXIubG93ZXIgPSBydCA8IHF1YXJ0X2R1cl9tcywgcnQub3V0bGllci51cHBlciA9IHJ0ID4gKGR1cl9tcyArIDEwMDAwKSkKCiMgQ2hlY2sgbGlzdCBvZiBvdXRsaWVycyB0aGF0IHdlcmUgcmVtb3ZlZApjb25kYXRhLnRlc3Qub3V0bGllcnMgPC0gY29uZGF0YS50ZXN0LmNoZWNrICU+JQogIHN1YnNldChydC5vdXRsaWVyLmxvd2VyID09IFRSVUUgfCBydC5vdXRsaWVyLnVwcGVyID09IFRSVUUpCnN1bW1hcnkoY29uZGF0YS50ZXN0Lm91dGxpZXJzKQoKIyBTdW1tYXJpemUgbnVtYmVyIG9mIG91dGxpZXJzIGF0dHJpYnV0ZWQgdG8gZWFjaCBwYXJ0aWNpcGFudApjb25kYXRhLm91dGxpZXJzLmJ5c3ViaiA8LSBjb25kYXRhLnRlc3Qub3V0bGllcnMgJT4lICNmaWx0ZXIoY29uZGl0aW9uSWQ9PSJjb25kQyIpICU+JQogICAgZ3JvdXBfYnkocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQpICU+JQogICAgY291bnQoc29ydD1UUlVFKQpjb25kYXRhLm91dGxpZXJzLmJ5c3ViagpgYGAKCmBgYHtyfQpWaWV3KGNvbmRhdGEudGVzdC5vdXRsaWVycykKYGBgCgoKYGBge3J9CiMgQQo0LzE2OCAjMC4wMjM4MDk1MiBzdWJqIDYwMDUzMjI3MTI1ZTUwNDE0MmRmOTFlOQoKIyBDCiMgODUvMTY4ICMwLjUwNTk1MjQgc3ViaiA1Njk5ZDI1NjI1ZDllOTAwMGRiMGM3Y2MgKipCVVQgQUxTTyBJTiBDT05EIEU/Pz8KMTQvMTY4ICMwLjA4MzMzMzMzIHN1YmogNWU0MmY3NGY1Yjc3MmExODQzNGNhYmY3IC0tLSBhbGwgd2VyZSBsb25nZXI7IGNhbiBrZWVwIGlmIG5lY2Vzc2FyeQo0LzE2OCAjMC4wMjM4MDk1MiBzdWJqIDVhNjhkMWMxYzBkODM2MDAwMTA4MjFlMAoKIyBFCiMgMTY4LzE2OCAjMC41MDU5NTI0IHN1YmogNTY5OWQyNTYyNWQ5ZTkwMDBkYjBjN2NjICoqQlVUIEFMU08gSU4gQ09ORCBDPz8/CiMgODUvMTY4ICMwLjUwNTk1MjQgNWZjZDBlZTQwNmJkN2FiOTRlY2M0MjRjIC0tLSB0aGUgc3Bva2FuZSAxOC15ZWFyLW9sZAo3LzE2OCAjMC4wNDE2NjY2NyBzdWJqIDVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MyAtLS0gYWxsIHdlcmUgc2hvcnRlci4uLjsgYWxzbyB0aGUgYWxpZW4gZ3V5CjUvMTY4ICMwLjAyOTc2MTkgNWZjNzY5ZGYxZjRlMjcwMTdhNjM4ZThlCjQvMTY4ICMwLjAyMzgwOTUyIHN1YmogNWZkZjlkMTNhNmE5ZWQ3ZDhlZmQwYjY5CgpgYGAKCkdvIGJhY2sgdG8gdG9wIGFuZCByZW1vdmUgb3V0bGllciBwYXJ0aWNpcGFudHMsIGlmIG5lY2Vzc2FyeS4gVGhlbiByZXJ1biBldmVyeXRoaW5nIHVwIHRvIHRoaXMgcG9pbnQuCgojIyMgRmluYWxpemUgUmVzdWx0cwoKYGBge3J9CiMgUmVtb3ZlIG91dGxpZXJzCmNvbmRhdGEudGVzdC5maW5hbCA8LSBzZXRkaWZmKGNvbmRhdGEudGVzdC5jaGVjaywgY29uZGF0YS50ZXN0Lm91dGxpZXJzKQoKIyBHcm91cCBkYXRhIGJ5IFN0aW1UeXBlIChpLmUuIFNwZWFrZXItU3BlYWtlckd1aXNlLVZvd2VsLVNlbnROdW0pCmNvbmRhdGEudGVzdC5maW5hbCA8LSBjb25kYXRhLnRlc3QuZmluYWwgJT4lCiAgdW5pdGUodG9rZW4sIHNwZWFrZXIsIHNlbnROdW0sIHJlbW92ZT1GQUxTRSkgJT4lCiAgCiAgbXV0YXRlKHdvcmQgPSBjYXNlX3doZW4odG9rZW4gPT0gIlMzXzIxIiB+ICJicmlnaHQiLAogICAgICAgICAgICAgICAgICAgdG9rZW4gPT0gIlMzXzIyIiB+ICJkZXZpY2UiLAogICAgICAgICAgICAgICAgICAgdG9rZW4gPT0gIlMzXzMwIiB+ICJ0d2ljZSIsCiAgICAgICAgICAgICAgICAgICB0b2tlbiA9PSAiUzlfMjMiIH4gImdvb2RuaWdodCIsCiAgICAgICAgICAgICAgICAgICB0b2tlbiA9PSAiUzlfMjUiIH4gImludml0ZSIsCiAgICAgICAgICAgICAgICAgICB0b2tlbiA9PSAiUzlfMjkiIH4gInNpZ2h0IiwKICAgICAgICAgICAgICAgICAgIHRva2VuID09ICJTM18xOCIgfiAic2xvdWNoIiwKICAgICAgICAgICAgICAgICAgIHRva2VuID09ICJTM18xOSIgfiAid2l0aG91dCIsCiAgICAgICAgICAgICAgICAgICB0b2tlbiA9PSAiUzNfMjAiIH4gIndvcmtvdXQiLAogICAgICAgICAgICAgICAgICAgdG9rZW4gPT0gIlM5XzExIiB+ICJjaGVja291dCIsCiAgICAgICAgICAgICAgICAgICB0b2tlbiA9PSAiUzlfMTgiIH4gInNwcm91dHMiLAogICAgICAgICAgICAgICAgICAgdG9rZW4gPT0gIlM5XzIwIiB+ICJ3b3Jrb3V0IikpICU+JQogIG11dGF0ZShpdGVtID0gcGFzdGUwKHNwZWFrZXIsIl8iLHdvcmQpKSAlPiUKICBtdXRhdGUocmVzcFJTID0gY2FzZV93aGVuKHJhaXNlZF9yZXNwb25zZSA9PSAidHJ1ZSIgfiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2VkX3Jlc3BvbnNlID09ICJmYWxzZSIgfiAwKSkgJT4lCiAgCiAgdW5pdGUoc3RpbVR5cGVfYnl3b3JkLCBzcGVha2VyLCBzcGVha2VyR3Vpc2UsIHZvd2VsLCB3b3JkLCByZW1vdmU9RkFMU0UpICU+JQogIHVuaXRlKHN0aW1UeXBlX2J5dm93ZWwsIHNwZWFrZXIsIHNwZWFrZXJHdWlzZSwgdm93ZWwsIHJlbW92ZT1GQUxTRSkgJT4lCiAgCiAgbXV0YXRlKHNlbnROdW0gPSBhcy5mYWN0b3Ioc2VudE51bSkpICU+JQogIG11dGF0ZShzdGVwID0gKHN0ZXAtNSkpICU+JQogIAogIHNlbGVjdChwYXJ0aWNpcGFudElkLCBndWlzZUNvbWJpbmF0aW9uLCBzdGVwLCB2b3dlbCwgc3BlYWtlckd1aXNlLCBzcGVha2VyLCB3b3JkLCBzcGVha2VyT3JkZXIsIHJlc3BSUywgc3RpbVR5cGVfYnl3b3JkLCBzdGltVHlwZV9ieXZvd2VsLCBldmVyeXRoaW5nKCkpCgojIFN1bW1hcnkKc3VtbWFyeShjb25kYXRhLnRlc3QuZmluYWwpCgojIFByaW50IGRhdGEKY29uZGF0YS50ZXN0LmZpbmFsCgojIFdyaXRlIHRvIGZpbGUKd3JpdGUuY3N2KGNvbmRhdGEudGVzdC5maW5hbCwgJ2RhdGEvYXhiXzFiX2V4cF9kYXRhLmNzdicsIHJvdy5uYW1lcz1GKQpgYGAKCmBgYHtyfQojIEZpbmFsIGtlcHQgZGF0YSBwb2ludHMKKGRhdGFwb2ludHMuZmluYWwgPC0gbnJvdyhjb25kYXRhLnRlc3QuZmluYWwpKQoKIyBGaW5hbCByZW1vdmVkIGRhdGEgcG9pbnRzCmRhdGFwb2ludHMub2ctZGF0YXBvaW50cy5maW5hbAoKIyBDYWxjdWxhdGUgcGVyY2VudGFnZSBvZiBkYXRhIHJlbW92ZWQKKGRhdGFwb2ludHMub2ctZGF0YXBvaW50cy5maW5hbCkvZGF0YXBvaW50cy5vZyAjID0gMC4wMDg4NTIyNTkgPSAwLjglIG9mIHRoZSBkYXRhIHdlcmUgcmVtb3ZlZCBkdWUgdG8gcmVzcG9uc2VzIHRoYXQgd2VyZSB0b28gcXVpY2sgKGxlc3MgdGhhbiAzLzQgb2YgdGhlIHRpbWUgaW50byB0aGUgYXVkaW8gZmlsZSwgYmVmb3JlIHRoZSB0aGlyZCB0b2tlbiB3b3VsZCBoYXZlIHBsYXllZCkgb3IgdG9vIHNsb3cgKG92ZXIgMTAgc2VjIGFmdGVyIHRoZSBlbmQgb2YgdGhlIGF1ZGlvIGZpbGUsIGFuIGFyYml0cmFyaWx5IGNob3NlbiB2YWx1ZSB0aGF0IHNob3VsZCBiZSBlbm91Z2ggdGltZSBpZiBhIHBhcnRpY2lwYW50IHdlcmUgcmVzcG9uZGluZyBhcyBxdWlja2x5IGFzIHBvc3NpYmxlKQpgYGAKCgojIyBTdW1tYXJpemUgRGF0YQojIyMgUGxvdHMKIyMjIyBQcm9wUlMgYnkgR3Vpc2UKYGBge3J9CiMgR2V0IHN1YmogbWVhbnMgcGVyIGNvbmRpdGlvbgpzdWJqLm1lYW5zIDwtIGNvbmRhdGEudGVzdC5maW5hbCAlPiUgI2ZpbHRlcihwYXJ0aWNpcGFudElkIT0nNWU0MmY3NGY1Yjc3MmExODQzNGNhYmY3JykgJT4lCiAgZ3JvdXBfYnkocGFydGljaXBhbnRJZCwgc3RlcCwgdm93ZWwsIHNwZWFrZXJHdWlzZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW4uUHJvcCA9IG1lYW4ocmVzcFJTKSkKCiMgR2V0IGdyb3VwIG1lYW5zIGFuZCBzZSBwZXIgY29uZGl0aW9uIChieSBhdmVyYWdpbmcgc3BlYWtlciBtZWFucykKY29uZGl0aW9uLm1lYW5zIDwtIHN1YmoubWVhbnMgJT4lCiAgZ3JvdXBfYnkoc3RlcCwgdm93ZWwsIHNwZWFrZXJHdWlzZSkgJT4lCiAgc3VtbWFyaXNlKGdyYW5kTS5Qcm9wID0gbWVhbihtZWFuLlByb3ApLCBzZSA9IHN0ZC5lcnJvcihtZWFuLlByb3ApKQoKIyBQbG90IGxpbmVwbG90IHdpdGggZXJyb3IgYmFycyBvbiBzdGVwIHBvaW50cwpieUd1aXNlX3Byb3BfcGxvdCA8LSBjb25kaXRpb24ubWVhbnMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RlcCwgeSA9IGdyYW5kTS5Qcm9wKSkgKwogIGdlb21fcG9pbnQoc3RhdD0iaWRlbnRpdHkiLCBhZXMoY29sb3VyID0gZmFjdG9yKHNwZWFrZXJHdWlzZSkpLCBjZXg9NSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyPWZhY3RvcihzcGVha2VyR3Vpc2UpLCBsaW5ldHlwZT1mYWN0b3Ioc3BlYWtlckd1aXNlKSksIGx3ZD0xKSArCiAgZ2VvbV9lcnJvcmJhcih3aWR0aCA9IC4yNSwgYWVzKHltaW4gPSBncmFuZE0uUHJvcC1zZSwgeW1heCA9IGdyYW5kTS5Qcm9wK3NlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gZmFjdG9yKHNwZWFrZXJHdWlzZSkpKSArCiAgZmFjZXRfZ3JpZCh+dm93ZWwpICsKICBsYWJzKHkgPSAiUHJvcG9ydGlvbiAncmFpc2VkJyByZXNwb25zZSIsIHggPSAiQ29udGludXVtIFN0ZXAgKFVSIHRvIFJTKSIsIGNvbG9yPSJHdWlzZSIsCiAgICAgICBsaW5ldHlwZSA9ICJHdWlzZSIsIHRpdGxlPSJSYWlzaW5nIFBlcmNlcHRpb246IEJ5IEd1aXNlIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMCwgMSkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gLTM6MykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDQsMyldKSArCiAgZ2dfdGhlbWUoKQpieUd1aXNlX3Byb3BfcGxvdApgYGAKCiMjIyMgUlQgYnkgR3Vpc2UKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMgR2V0IHN1YmogbWVhbnMgcGVyIGNvbmRpdGlvbgpzdWJqLm1lYW5zIDwtIGNvbmRhdGEudGVzdC5maW5hbCAlPiUgZmlsdGVyKHNwZWFrZXI9PSdTMycpICU+JSAjZmlsdGVyKHBhcnRpY2lwYW50SWQhPSc1ZTQyZjc0ZjViNzcyYTE4NDM0Y2FiZjcnKSAlPiUKICBncm91cF9ieShwYXJ0aWNpcGFudElkLCBzdGVwLCB2b3dlbCwgc3BlYWtlckd1aXNlKSAlPiUKICBzdW1tYXJpc2UobWVhbi5ydCA9IG1lYW4obG9nKHJ0KSkpCgojIEdldCBncm91cCBtZWFucyBhbmQgc2UgcGVyIGNvbmRpdGlvbiAoYnkgYXZlcmFnaW5nIHNwZWFrZXIgbWVhbnMpCmNvbmRpdGlvbi5tZWFucyA8LSBzdWJqLm1lYW5zICU+JQogIGdyb3VwX2J5KHN0ZXAsIHZvd2VsLCBzcGVha2VyR3Vpc2UpICU+JQogIHN1bW1hcmlzZShncmFuZE0ucnQgPSBtZWFuKG1lYW4ucnQpLCBzZSA9IHN0ZC5lcnJvcihtZWFuLnJ0KSkKCiMgUGxvdCBsaW5lcGxvdCB3aXRoIGVycm9yIGJhcnMgb24gc3RlcCBwb2ludHMKYnlHdWlzZV9ydF9wbG90IDwtIGNvbmRpdGlvbi5tZWFucyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzdGVwLCB5ID0gZ3JhbmRNLnJ0KSkgKwogIGdlb21fcG9pbnQoc3RhdD0iaWRlbnRpdHkiLCBhZXMoY29sb3VyID0gZmFjdG9yKHNwZWFrZXJHdWlzZSkpLCBjZXg9NSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyPWZhY3RvcihzcGVha2VyR3Vpc2UpLCBsaW5ldHlwZT1mYWN0b3Ioc3BlYWtlckd1aXNlKSksIGx3ZD0xKSArCiAgZ2VvbV9lcnJvcmJhcih3aWR0aCA9IC4yNSwgYWVzKHltaW4gPSBncmFuZE0ucnQtc2UsIHltYXggPSBncmFuZE0ucnQrc2UsIGNvbG91ciA9IGZhY3RvcihzcGVha2VyR3Vpc2UpKSkgKwogIGZhY2V0X2dyaWQofnZvd2VsKSArCiAgbGFicyh5ID0gIkxvZyBSZXNwb25zZSBUaW1lIiwgeCA9ICJDb250aW51dW0gU3RlcCAoVVIgdG8gUlMpIiwgY29sb3I9Ikd1aXNlIiwKICAgICAgIGxpbmV0eXBlID0gIkd1aXNlIiwgdGl0bGU9IlJlYWN0aW9uIFRpbWU6IEJ5IEd1aXNlIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDQsMyldKSsKICBnZ190aGVtZSgpCmJ5R3Vpc2VfcnRfcGxvdApgYGAKCiMjIyMgUHJvcFJTIGJ5IFdvcmQKYGBge3J9CiMgR2V0IHN1YmogbWVhbnMgcGVyIGNvbmRpdGlvbgpzdWJqLm1lYW5zIDwtIGNvbmRhdGEudGVzdC5maW5hbCAlPiUgZmlsdGVyKHNwZWFrZXI9PSJTMyIpICU+JQogIGdyb3VwX2J5KHBhcnRpY2lwYW50SWQsIHN0ZXAsIHZvd2VsLCBzcGVha2VyR3Vpc2UsIHNwZWFrZXIsIHdvcmQpICU+JQogIHN1bW1hcmlzZShtZWFuLlByb3AgPSBtZWFuKHJlc3BSUykpCgojIEdldCBncm91cCBtZWFucyBhbmQgc2UgcGVyIGNvbmRpdGlvbiAoYnkgYXZlcmFnaW5nIHNwZWFrZXIgbWVhbnMpCmNvbmRpdGlvbi5tZWFucyA8LSBzdWJqLm1lYW5zICU+JQogIGdyb3VwX2J5KHN0ZXAsIHZvd2VsLCBzcGVha2VyR3Vpc2UsIHNwZWFrZXIsIHdvcmQpICU+JQogIHN1bW1hcmlzZShncmFuZE0uUHJvcCA9IG1lYW4obWVhbi5Qcm9wKSwgc2UgPSBzdGQuZXJyb3IobWVhbi5Qcm9wKSkKCiMgQUkKYnlXb3JkX3Byb3BfcGxvdCA8LSBjb25kaXRpb24ubWVhbnMgJT4lIGZpbHRlcih2b3dlbD09IkFJIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RlcCwgeSA9IGdyYW5kTS5Qcm9wKSkgKwogIGdlb21fcG9pbnQoc3RhdD0iaWRlbnRpdHkiLCBhZXMoY29sb3VyID0gZmFjdG9yKHNwZWFrZXJHdWlzZSkpLCBjZXg9NSwgYWxwaGE9MC43NSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyPWZhY3RvcihzcGVha2VyR3Vpc2UpLCBsaW5ldHlwZT1mYWN0b3Iod29yZCkpLCBsd2Q9MSkgKwogIGdlb21fZXJyb3JiYXIod2lkdGggPSAuMjUsIGFlcyh5bWluID0gZ3JhbmRNLlByb3Atc2UsIHltYXggPSBncmFuZE0uUHJvcCtzZSwgY29sb3VyID0gZmFjdG9yKHNwZWFrZXJHdWlzZSkpKSArCiAgZmFjZXRfZ3JpZChzcGVha2VyfndvcmQpICsKICBsYWJzKHkgPSAiUHJvcG9ydGlvbiAncmFpc2VkJyByZXNwb25zZSIsIHggPSAiQ29udGludXVtIFN0ZXAgKFVSIHRvIFJTKSIsIGNvbG9yPSJHdWlzZSIsCiAgICAgICBsaW5ldHlwZSA9ICJXb3JkIiwgdGl0bGU9IkFJIFJhaXNpbmcgUGVyY2VwdGlvbjogQnkgV29yZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IC0zOjMpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNiw0LDMpXSkrCiAgZ2dfdGhlbWUoKQpieVdvcmRfcHJvcF9wbG90CgojIEFVCmJ5V29yZF9wcm9wX3Bsb3QgPC0gY29uZGl0aW9uLm1lYW5zICU+JSBmaWx0ZXIodm93ZWw9PSJBVSIpICU+JQogIGdncGxvdChhZXMoeCA9IHN0ZXAsIHkgPSBncmFuZE0uUHJvcCkpICsKICBnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5IiwgYWVzKGNvbG91ciA9IGZhY3RvcihzcGVha2VyR3Vpc2UpKSwgY2V4PTUsIGFscGhhPTAuNzUpICsKICBnZW9tX2xpbmUoYWVzKGNvbG91cj1mYWN0b3Ioc3BlYWtlckd1aXNlKSwgbGluZXR5cGU9ZmFjdG9yKHdvcmQpKSwgbHdkPTEpICsKICBnZW9tX2Vycm9yYmFyKHdpZHRoID0gLjI1LCBhZXMoeW1pbiA9IGdyYW5kTS5Qcm9wLXNlLCB5bWF4ID0gZ3JhbmRNLlByb3Arc2UsIGNvbG91ciA9IGZhY3RvcihzcGVha2VyR3Vpc2UpKSkgKwogIGZhY2V0X2dyaWQoc3BlYWtlcn53b3JkKSArCiAgbGFicyh5ID0gIlByb3BvcnRpb24gJ3JhaXNlZCcgcmVzcG9uc2UiLCB4ID0gIkNvbnRpbnV1bSBTdGVwIChVUiB0byBSUykiLCBjb2xvcj0iR3Vpc2UiLAogICAgICAgbGluZXR5cGUgPSAiV29yZCIsIHRpdGxlPSJBVSBSYWlzaW5nIFBlcmNlcHRpb246IEJ5IFdvcmQiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW09YygwLCAxKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAtMzozKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1naGlibGlfcGFsZXR0ZSgiUG9ueW9NZWRpdW0iKVtjKDYsNCwzKV0pKwogIGdnX3RoZW1lKCkKYnlXb3JkX3Byb3BfcGxvdAoKYGBgCgojIyMjIFByb3BSUyBieSBJbmRpdmlkdWFsCihhZGFwdGVkIGZyb20gQ2FudG9NZXJnZXJzIHByb2plY3QpCmBgYHtyLCBlY2hvPUZ9CiMgTUkgZ3Vpc2UKYnlTdWJqX3Byb3BfcGxvdCA8LSBjb25kYXRhLnRlc3QuZmluYWwgJT4lIGZpbHRlcih2b3dlbD09IkFVIikgJT4lIGZpbHRlcihjb25kaXRpb25JZD09ImNvbmRBIikgJT4lCiAgZ2dwbG90KGFlcyh4PXN0ZXAsIHk9cmVzcFJTLCBjb2xvcj1zcGVha2VyR3Vpc2UpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpICsKICBmYWNldF93cmFwKH5wYXJ0aWNpcGFudElkKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgYWxwaGE9MC41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gIDAuNSwgYWxwaGEgPSAwLjUpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IC0zOjMpICsKICBsYWJzKHRpdGxlPSJBVSBSYWlzaW5nIHBlcmNlcHRpb246IEJ5IFBhcnRpY2lwYW50IiwgeT0iUHJvcG9ydGlvbiBSUyByZXNwb25zZSIsIGNvbG9yPSJHdWlzZSIsIHg9IiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoMyldKSsKICBnZ190aGVtZSgpCmJ5U3Vial9wcm9wX3Bsb3QKCiMgQ04gZ3Vpc2UKYnlTdWJqX3Byb3BfcGxvdCA8LSBjb25kYXRhLnRlc3QuZmluYWwgJT4lIGZpbHRlcih2b3dlbD09IkFVIikgJT4lIGZpbHRlcihjb25kaXRpb25JZD09ImNvbmRDIikgJT4lCiAgZ2dwbG90KGFlcyh4PXN0ZXAsIHk9cmVzcFJTLCBjb2xvcj1zcGVha2VyR3Vpc2UpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpICsKICBmYWNldF93cmFwKH5wYXJ0aWNpcGFudElkKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgYWxwaGE9MC41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gIDAuNSwgYWxwaGEgPSAwLjUpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IC0zOjMpICsKICBsYWJzKHRpdGxlPSJBVSBSYWlzaW5nIHBlcmNlcHRpb246IEJ5IFBhcnRpY2lwYW50IiwgeT0iUHJvcG9ydGlvbiBSUyByZXNwb25zZSIsIGNvbG9yPSJHdWlzZSIsIHg9IiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNCldKSsKICBnZ190aGVtZSgpCmJ5U3Vial9wcm9wX3Bsb3QKCiMgQkwgZ3Vpc2UKYnlTdWJqX3Byb3BfcGxvdCA8LSBjb25kYXRhLnRlc3QuZmluYWwgJT4lIGZpbHRlcih2b3dlbD09IkFVIikgJT4lIGZpbHRlcihjb25kaXRpb25JZD09ImNvbmRFIikgJT4lCiAgZ2dwbG90KGFlcyh4PXN0ZXAsIHk9cmVzcFJTLCBjb2xvcj1zcGVha2VyR3Vpc2UpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpICsKICBmYWNldF93cmFwKH5wYXJ0aWNpcGFudElkKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgYWxwaGE9MC41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gIDAuNSwgYWxwaGEgPSAwLjUpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IC0zOjMpICsKICBsYWJzKHRpdGxlPSJBVSBSYWlzaW5nIHBlcmNlcHRpb246IEJ5IFBhcnRpY2lwYW50IiwgeT0iUHJvcG9ydGlvbiBSUyByZXNwb25zZSIsIGNvbG9yPSJHdWlzZSIsIHg9IiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoMildKSsKICBnZ190aGVtZSgpCmJ5U3Vial9wcm9wX3Bsb3QKYGBgCgojIC4uLgojIFF1ZXN0aW9ubmFpcmUgRGF0YQojIyBQcmUtUHJvY2VzcyBEYXRhCiMjIyBSZWFkICYgQ2xlYW4gUmVzdWx0cwpUaGUgUXVhbHRyaWNzIG91dHB1dCBpbmNsdWRlcyBhIHRleHQgcmVzcG9uc2UgZmlsZSBhbmQgYSBudW1lcmljYWwgcmVzcG9uc2UgZmlsZS4gQmVjYXVzZSBJIHdhbnQgdG8gdXNlIHRleHQgZm9yIHNvbWUgcXVlc3Rpb25zIChlLmcuIElLMiwgdGhlIHdvcmQgc2VsZWN0aW9uIHF1ZXN0aW9uKSBidXQgbnVtYmVycyBmb3Igb3RoZXIgcXVlc3Rpb25zIChlLmcuIGZhbWlsaWFyaXR5IHNjYWxlIHF1ZXN0aW9ucyksIEkgbmVlZCB0byB3b3JrIHdpdGggYm90aC4KCkkgaGF2ZSBkb3dubG9hZGVkIHRoZSBmaWxlcyBhbmQgcmVuYW1lZCB0aGVtIGFzICdfdGV4dCcgYW5kICdfbnVtIiBmaWxlcy4gU3BlY2lmaWNhbGx5LCBmaWxlIG5hbWVzIGhhdmUgYmVlbiByZW5hbWVkIGZyb20gdGhlIG9yaWdpbmFsIFF1YWx0cmljcyBkb3dubG9hZCBuYW1lIHRvIGBmaW5hbF9sYnFfbnVtLmNzdmAgYW5kIGBmaW5hbF9sYnFfdGV4dC5jc3ZgLgpgYGB7ciwgd2FybmluZz1GfQojIExpc3QgcmVzdWx0cyBmaWxlcyBwZXIgc3ViamVjdApmaWxlbGlzdCA8LSBsaXN0LmZpbGVzKHBhdGg9Ii4vZGF0YS9heGJfMWIvcXVlc3Rpb25uYWlyZS8iLCBwYXR0ZXJuPSIuY3N2IixmdWxsLm5hbWVzPVRSVUUpCgojIFJlYWQgYW5kIENvbmNhdGVuYXRlIHJlc3VsdHMKIyAoMSkgSnVzdCByZWFkIGFuZCBjb25jYXQKI2NvbmRhdGEucmVhZCA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoZmlsZWxpc3QsIHJlYWQuY3N2KSkKCiMgKDIpIFJlYWQsIGNvbmNhdCBhbmQgZXh0cmFjdCBwYXJ0IG9mIGZpbGVuYW1lCnF1ZXNkYXRhLnJlYWQgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KGZpbGVsaXN0LCBmdW5jdGlvbih4KSBjYmluZChyZWFkLmNzdih4KSwgZGF0YUZvcm1hdD1zdHJzcGxpdChnc3ViKCIuY3N2IiwiIix4KSwgIl8iKVtbMV1dWzRdKSkpCgojIENoZWNrIHRoYXQgbmV3bHkgZXh0cmFjdGVkIGNvbHVtbnMgYXJlIGNvcnJlY3QgKGBudW1gIG9yIGB0ZXh0YCkKIyBxdWVzZGF0YS5yZWFkJGRhdGFGb3JtYXQKCmNvbG5hbWVzKHF1ZXNkYXRhLnJlYWQpCgpgYGAKClJlbW92ZSB0aGUgdW5uZWNlc3NhcmlseSBjb2x1bW5zIGFuZCByb3dzLiBBbHNvIGV4dHJhY3QgdGhlIHF1ZXN0aW9uIHRleHQgd2hpbGUgd2UncmUgYXQgaXQgKGJlZm9yZSByZW1vdmluZyB0aGUgcm93cykgc28gdGhhdCB3ZSBjYW4gcmVmZXIgdG8gdGhlIHF1ZXN0aW9ucyBhcyBuZWNlc3NhcnksIGJ1dCB0aGV5IHdvbid0IGJlIGluIHRoZSBkYXRhIHRvIGJlIGFuYWx5c2VkLgpgYGB7cn0KIyBSZW1vdmUgbWV0YWRhdGEgY29sdW1ucyAoZmlyc3Qgc2V2ZXJhbCkKcXVlc2RhdGEgPC0gcXVlc2RhdGEucmVhZCAlPiUgCiAgc2VsZWN0KC1jKFN0YXJ0RGF0ZSwgRW5kRGF0ZSwgU3RhdHVzLCBJUEFkZHJlc3MsIFByb2dyZXNzLCBEdXJhdGlvbi4uaW4uc2Vjb25kcy4sIEZpbmlzaGVkLCBSZWNvcmRlZERhdGUsIFJlc3BvbnNlSWQsIFJlY2lwaWVudExhc3ROYW1lLCBSZWNpcGllbnRGaXJzdE5hbWUsIFJlY2lwaWVudEVtYWlsLCBFeHRlcm5hbFJlZmVyZW5jZSwgTG9jYXRpb25MYXRpdHVkZSwgTG9jYXRpb25Mb25naXR1ZGUsIERpc3RyaWJ1dGlvbkNoYW5uZWwsIFVzZXJMYW5ndWFnZSwgUFJPTElGSUNfUElEKSkKCiMgUXVlc3Rpb24gcmVmZXJlbmNlIChpZiB3YW50IHRvIGxvb2sgYmFjayBhdCBxdWVzdGlvbiB0ZXh0KQpxdWVzdGlvbnMgPC0gcXVlc2RhdGEgJT4lIHNsaWNlKDEpCgojIFJlbW92ZSB1bmVjZXNzYXJ5IHF1ZXN0aW9uIGhlYWRlciBhbmQgdGVzdCBkYXRhIHJvd3MgKyBhZGQvZml4IHJlbGV2YW50IGluZm8gKyByZW1vdmUgZGF0YSBmcm9tIGRyb3BwZWQgc3ViamVjdHMKcXVlc2RhdGEgPC0gcXVlc2RhdGEgJT4lCiAgIyBSZW1vdmUgaXJyZWxldmFudCByb3dzIGFuZCByZW1vdmVkIGRhdGEKICBzdWJzZXQoc3ViaklEICE9ICJQbGVhc2UgY2hlY2sgdGhhdCB5b3VyIFByb2xpZmljIElEIGlzIGNvcnJlY3QsIHRoZW4gcHJlc3MgdGhlICduZXh0JyBidXR0b24gdG8gY29udGludWUgd2l0aCB0aGUgc3VydmV5LiIgJiBzdWJqSUQgIT0ie1wiSW1wb3J0SWRcIjpcIlFJRDc4X1RFWFRcIn0iKSAlPiUKICBzdWJzZXQoc3ViaklEICE9ICJwcmV2aWV3MSIpICU+JQoKICAjIE1lcmdlIHdpdGggcGVyY2VwdGlvbiBkYXRhIChmb3IgY29udmVuaWVuY2UsIHVzZXMgdGhlIG1pbmltYWwgZGF0YWZyYW1lIGBjb25kYXRhLnRvbmVzYCkKICAjIyBBZGRzIHN1YmplY3QgYW5kIGNvbmRpdGlvbiBpbmZvICsgYXV0b21hdGljYWxseSBkcm9wcyBzdWJqZWN0cyB0aGF0IHdlcmUgc2NyZWVuZWQgb3V0IGJhc2VkIG9uIHBlcmNlcHRpb24gZXhwZXJpbWVudCBwZXJmb3JtYW5jZS9yZXR1cm5zCiAgcmVuYW1lKHBhcnRpY2lwYW50SWQgPSBzdWJqSUQpICU+JQogIG1lcmdlKC4sIGNvbmRhdGEudG9uZXMpICU+JQogIG11dGF0ZShndWlzZUNvbWJpbmF0aW9uID0gY2FzZV93aGVuKGNvbmRpdGlvbklkID09ICJjb25kQSIgfCBjb25kaXRpb25JZCA9PSAiY29uZEIiIH4gIm1hdGNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbklkID09ICJjb25kQyIgfCBjb25kaXRpb25JZCA9PSAiY29uZEQiIH4gIm1pc21hdGNoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbklkID09ICJjb25kRSIgfCBjb25kaXRpb25JZCA9PSAiY29uZEYiIH4gImJhc2VsaW5lIikpICU+JQogIG11dGF0ZShzcGVha2VyT3JkZXIgPSBjYXNlX3doZW4oY29uZGl0aW9uSWQgPT0gImNvbmRBIiB8IGNvbmRpdGlvbklkID09ICJjb25kQyIgfCBjb25kaXRpb25JZCA9PSAiY29uZEUiIH4gIlMzLVM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbklkID09ICJjb25kQiIgfCBjb25kaXRpb25JZCA9PSAiY29uZEQiIHwgY29uZGl0aW9uSWQgPT0gImNvbmRGIiB+ICJTOS1TMyIpKSAlPiUKICBkcm9wbGV2ZWxzKCkKCiMgRml4IGFuc3dlcnMgZnJvbSB0aGUgb25lICdhbGllbicgcGFydGljaXBhbnQKcXVlc2RhdGEgPC0gcXVlc2RhdGEgJT4lCiAgbXV0YXRlKEdlbmRlciA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ20nLCBHZW5kZXIpLAogICAgICAgICBMaW5nRXhwID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnbm9uZScsIExpbmdFeHApLAogICAgICAgICBMb2MxXzEgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICcwJywgTG9jMV8xKSwKICAgICAgICAgTG9jMV8yID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnMScsIExvYzFfMiksCiAgICAgICAgIExvYzFfMyA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ3plZWxhbmQnLCBMb2MxXzMpLAogICAgICAgICBMb2MxXzQgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICdvdHRhd2EnLCBMb2MxXzQpLAogICAgICAgICBMb2MxXzUgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICdtaScsIExvYzFfNSksCiAgICAgICAgIExvYzJfMSA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJzEnLCBMb2MyXzEpLAogICAgICAgICBMb2MyXzIgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICcyJywgTG9jMl8yKSwKICAgICAgICAgTG9jMl8zID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnc2Vkb25hJywgTG9jMl8zKSwKICAgICAgICAgTG9jMl80ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnJywgTG9jMl80KSwKICAgICAgICAgTG9jMl81ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnYXJpem9uYScsIExvYzJfNSksCiAgICAgICAgIExvYzNfMSA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJzInLCBMb2MzXzEpLAogICAgICAgICBMb2MzXzIgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICcxNCcsIExvYzNfMiksCiAgICAgICAgIExvYzNfMyA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ2dsZW5uJywgTG9jM18zKSwKICAgICAgICAgTG9jM180ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnYWxsZWdhbicsIExvYzNfNCksCiAgICAgICAgIExvYzNfNSA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ21pJywgTG9jM181KSwKICAgICAgICAgTG9jNF8xID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnMTQnLCBMb2M0XzEpLAogICAgICAgICBMb2M0XzIgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICcxOCcsIExvYzRfMiksCiAgICAgICAgIExvYzRfMyA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ3NvdXRoIGhhdmVuJywgTG9jNF8zKSwKICAgICAgICAgTG9jNF80ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAndmFuIGJ1cmVuJywgTG9jNF80KSwKICAgICAgICAgTG9jNV81ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnbWknLCBMb2M0XzUpLAogICAgICAgICBMb2M1XzEgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICcxOCcsIExvYzVfMSksCiAgICAgICAgIExvYzVfMiA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJzIzJywgTG9jNV8yKSwKICAgICAgICAgTG9jNV8zID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnZ3JhbmQgcmFwaWRzJywgTG9jNV8zKSwKICAgICAgICAgTG9jNV80ID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAna2VudCcsIExvYzVfNCksCiAgICAgICAgIExvYzVfNSA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ21pJywgTG9jNV81KSwKICAgICAgICAgTG9jNl8xID0gaWZlbHNlKHBhcnRpY2lwYW50SWQ9PSc1ZmM1MTFjMzJiOWJjNjBiYzE4OWI4OTMnLCAnMjMnLCBMb2M2XzEpLAogICAgICAgICBMb2M2XzIgPSBpZmVsc2UocGFydGljaXBhbnRJZD09JzVmYzUxMWMzMmI5YmM2MGJjMTg5Yjg5MycsICczNCcsIExvYzZfMiksCiAgICAgICAgIExvYzZfMyA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ3ZhcmlvdXMuLi4gZ3JhbmR2aWxsZScsIExvYzZfMyksCiAgICAgICAgIExvYzZfNCA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ3ZhcmlvdXMuLi4ga2VudCcsIExvYzZfNCksCiAgICAgICAgIExvYzZfNSA9IGlmZWxzZShwYXJ0aWNpcGFudElkPT0nNWZjNTExYzMyYjliYzYwYmMxODliODkzJywgJ3ZhcmlvdXMuLi4gbWknLCBMb2M2XzUpKQpgYGAKCiMjIyMgUXVlc3Rpb24gV29yZGluZwpIZXJlJ3MgdGhlIHRhYmxlIG9mIHRoZSBxdWVzdGlvbiB0YWdzLCBudW1iZXJzIGFuZCB0ZXh0LiBUaGlzIGludGVyYWN0aXZlIHRhYmxlIGFsbG93cyBmb3Igc29ydGluZyBhbmQgc2VhcmNoaW5nISBXZSBjYW4gdXNlIHRoaXMgdG8gY2hlY2sgdGhlIGV4YWN0IHdvcmRpbmcgb2YgdGhlIHF1ZXN0aW9uc+KAlGFsbCBvZiB0aGVtIGFzIHRoZXkgd2VyZSBzaG93biB0byB0aGUgcGFydGljaXBhbnQuCgpgYGB7ciwgZXZhbD1ULCByZXN1bHRzPSdhc2lzJ30KIyBQcmludCBxdWVzdGlvbnMgZm9yIHJlZmVyZW5jZQpEVDo6ZGF0YXRhYmxlKHF1ZXN0aW9ucywgCiAgICAgICAgICAgICAgb3B0aW9ucz1saXN0KHNjcm9sbFggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvV2lkdGggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KHdpZHRoID0gJzIwMHB4JywgdGFyZ2V0cyA9ICJfYWxsIikpKSkKYGBgCgojIyMgUHJvY2VzcyBEZW1vZ3JhcGhpYyBJbmZvCmBgYHtyfQojIFN1YnNldCB0byBkZW1vZ3JhcGhpYyBxdWVzdGlvbiBjb2x1bW5zCnF1ZXNkYXRhLmRlbW8gPC0gcXVlc2RhdGEgJT4lIGZpbHRlcihkYXRhRm9ybWF0ID09ICJ0ZXh0IikgJT4lCiAgc2VsZWN0KGNvbmRpdGlvbklkLCBwYXJ0aWNpcGFudElkLCBndWlzZUNvbWJpbmF0aW9uLCBzcGVha2VyT3JkZXIsIAogICAgICAgICBBZ2UsIEdlbmRlciwgRXRobmljaXR5LCBTcEhEaXNvcmRlciwgU3BIRGlzb3JkZXJfMl9URVhULCBEZWdyZWUsIExpbmdFeHAsIEZpcnN0TGFuZywgCiAgICAgICAgIExvYzFfMTpMb2M2XzUpICU+JQogIAogICMgRml4IHNwZWxsaW5nIGVycm9ycyBhbmQgdmFyaWF0aW9uIG9uIGRlbW9ncmFwaGljIHF1ZXN0aW9ucwogIG11dGF0ZV9hdCh2YXJzKC1jb25kaXRpb25JZCwgLXNwZWFrZXJPcmRlciksdG9sb3dlcikgJT4lCiAgbXV0YXRlX2lmKGlzLmNoYXJhY3Rlciwgc3RyX3RyaW0pCgojIENoZWNrIGRhdGEKcXVlc2RhdGEuZGVtbwoKIyBTdW1tYXJ5CiMgc3VtbWFyeShxdWVzZGF0YS5kZW1vKQpgYGAKCiMjIyMgQ2hlY2sgVmFsdWVzCmBgYHtyfQojIFF1aWNrIGNoZWNrIHZpYSB0YWJsZQpxdWVzLmRlbW8uc3VtIDwtIHF1ZXNkYXRhLmRlbW8gJT4lICBzZWxlY3QoY29uZGl0aW9uSWQsIEFnZSwgR2VuZGVyLCBFdGhuaWNpdHksIExvYzFfNSkKYGBgCgpgYGB7cn0Kd2l0aChxdWVzZGF0YS5kZW1vLCB1bmlxdWUoR2VuZGVyKSkKYGBgCgoKYGBge3J9CndpdGgocXVlc2RhdGEuZGVtbywgdW5pcXVlKExvYzFfNSkpCmBgYAoKYGBge3J9CndpdGgocXVlc2RhdGEuZGVtbywgdW5pcXVlKEV0aG5pY2l0eSkpCmBgYAoKIyMjIyBDbGVhbiBGcmVlIFJlc3BvbnNlcwpgYGB7cn0KcXVlc2RhdGEuZGVtbyA8LSBxdWVzZGF0YS5kZW1vICU+JQogIAogIG11dGF0ZShHZW5kZXIgPSBtZ3N1YihHZW5kZXIsIGMoIndvbWFuLip8ZmVtYWxlfGNpcyBmZW1hbGUiLCAibWFsZXxjaXMgbWFsZSIsICJub24tYmluYXJ5fG5vbiBiaW5hcnl8bm9uYmluYXJ5IiksIGMoImYiLCAibSIsICJuYiIpKSkgJT4lCiAgCiAgbXV0YXRlX2F0KHZhcnMoc3RhcnRzX3dpdGgoIkxvYyIpICYgZW5kc193aXRoKCJfNCIpKSwgfiBtZ3N1YigueCwgYygibWljaGlnLioifCJtaVsvLCBdLioiKSwgYygibWkiKSkpICU+JQogIAogIG11dGF0ZShFdGhuaWNpdHkgPSBtZ3N1YihFdGhuaWNpdHksIGMoImNhdWNhc2lhbnxjYXVjYXNpb258YW1lcmljYW58bWV6em9naW9ybm8iLCJhZnJpY2FuIGFtZXJpY2FufGFmcmljYW4tYW1lcmljYW4iLCAibWlkZGxlIGVhc3RlbiIpLCBjKCJ3aGl0ZSIsICJibGFjayIsICJtaWRkbGUtZWFzdGVybiIpKSkgJT4lCiAgbXV0YXRlKEV0aG5pY2l0eSA9IG1nc3ViKEV0aG5pY2l0eSwgYygiYXNpYW4vd2hpdGUiLCJ3aGl0ZS9oaXNwYW5pYyIpLCBjKCJtdWx0aXJhY2lhbCIsIm11bHRpcmFjaWFsIiksIGZpeGVkPVRSVUUpKSAlPiUKICBtdXRhdGUoRXRobmljaXR5ID0gbWdzdWIoRXRobmljaXR5LCBjKCIuKndoaXRlLioiLCAiYmxhY2suKiIsICIuKmFzaWFuIiksIGMoIndoaXRlIiwgImJsYWNrIiwgImFzaWFuIikpKSAlPiUKICAKICAjIENoYW5nZSB2ZWN0b3IgY2xhc3NlcyBmcm9tIGNoYXJhY3RlciBjbGFzcwogIG11dGF0ZV9hdCh2YXJzKEFnZSksIGFzLm51bWVyaWMpICU+JQogIAogICMgU2VsZWN0CiAgc2VsZWN0KGNvbmRpdGlvbklkLCBwYXJ0aWNpcGFudElkLCBldmVyeXRoaW5nKCkpCiAgCnF1ZXNkYXRhLmRlbW8KYGBgCgojIyMjIERlc2NyaXB0aXZlIFN0YXRzCmBgYHtyfQojIEV0aG5pY2l0eSBDb3VudHMKcXVlc2RhdGEuZGVtbyAlPiUgZ3JvdXBfYnkoRXRobmljaXR5KSAlPiUgY291bnQoKSAKCnF1ZXNkYXRhLmRlbW8gJT4lIGdyb3VwX2J5KEV0aG5pY2l0eSwgY29uZGl0aW9uSWQpICU+JSBjb3VudCgpICU+JSBwaXZvdF93aWRlcihFdGhuaWNpdHksIG5hbWVzX2Zyb20gPSBjb25kaXRpb25JZCwgdmFsdWVzX2Zyb209bikKCmBgYApgYGB7cn0KIyBHZW5kZXIgQ291bnRzCnF1ZXNkYXRhLmRlbW8gJT4lIGdyb3VwX2J5KEdlbmRlcikgJT4lIGNvdW50KCkKCnF1ZXNkYXRhLmRlbW8gJT4lIGdyb3VwX2J5KEdlbmRlciwgY29uZGl0aW9uSWQpICU+JSBjb3VudCgpICU+JSBwaXZvdF93aWRlcihHZW5kZXIsIG5hbWVzX2Zyb20gPSBjb25kaXRpb25JZCwgdmFsdWVzX2Zyb209bikKCmBgYAoKYGBge3J9CiMgQWdlCnF1ZXNkYXRhLmRlbW8gJT4lIHN1bW1hcmlzZShuPWxlbmd0aChjb25kaXRpb25JZCksIG1pbkFnZSA9IG1pbihBZ2UpLCBtYXhBZ2UgPSBtYXgoQWdlKSwgbWVhbkFnZSA9IG1lYW4oQWdlKSwgc2RBZ2UgPSBzZChBZ2UpKQoKcXVlc2RhdGEuZGVtbyAlPiUgZ3JvdXBfYnkoY29uZGl0aW9uSWQpICU+JQogIHN1bW1hcmlzZShuPWxlbmd0aChjb25kaXRpb25JZCksIG1pbkFnZSA9IG1pbihBZ2UpLCBtYXhBZ2UgPSBtYXgoQWdlKSwgbWVhbkFnZSA9IG1lYW4oQWdlKSwgc2RBZ2UgPSBzZChBZ2UpKSAlPiUgdW5ncm91cCgpCgpgYGAKCiMjIyMgTG9jYXRpb25zIExpc3QgKHVuZGVyIGRldikKYGBge3J9CnF1ZXNkYXRhLmxvYyA8LSBxdWVzZGF0YS5kZW1vICU+JSBzZWxlY3QocGFydGljaXBhbnRJZCwgc3RhcnRzX3dpdGgoIkxvYyIpKQpxdWVzZGF0YS5sb2MKCmxvYy5jb2RlIDwtIGRhdGEuZnJhbWUoTG9jID0gdW5pcXVlKChxdWVzZGF0YS5sb2MkTG9jMV8zKSkpICU+JQogIHJiaW5kKGRhdGEuZnJhbWUoTG9jID0gdW5pcXVlKChxdWVzZGF0YS5sb2MkTG9jMl8zKSkpKSAlPiUKICByYmluZChkYXRhLmZyYW1lKExvYyA9IHVuaXF1ZSgocXVlc2RhdGEubG9jJExvYzNfMykpKSkgJT4lCiAgcmJpbmQoZGF0YS5mcmFtZShMb2MgPSB1bmlxdWUoKHF1ZXNkYXRhLmxvYyRMb2M0XzMpKSkpICU+JQogIHJiaW5kKGRhdGEuZnJhbWUoTG9jID0gdW5pcXVlKChxdWVzZGF0YS5sb2MkTG9jNV8zKSkpKSAlPiUKICByYmluZChkYXRhLmZyYW1lKExvYyA9IHVuaXF1ZSgocXVlc2RhdGEubG9jJExvYzZfMykpKSkgJT4lCiAgdW5pcXVlKCkKbG9jLmNvZGUKYGBgCgoKIyMjIFByb2Nlc3MgVGV4dCBSZXNwb25zZXMKYGBge3IgbGJxZGF0YS10ZXh0fQojIFN1YnNldCBkYXRhIHRvIHRleHQgZm9ybWF0CnF1ZXNkYXRhLnRleHQgPC0gcXVlc2RhdGEgJT4lIGZpbHRlcihkYXRhRm9ybWF0ID09ICJ0ZXh0IikgJT4lCiAgc2VsZWN0KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkLCBndWlzZUNvbWJpbmF0aW9uLCBzcGVha2VyT3JkZXIsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgc2VsZWN0KC1jKEFnZSwgR2VuZGVyLCBFdGhuaWNpdHksIFNwSERpc29yZGVyLCBTcEhEaXNvcmRlcl8yX1RFWFQsIERlZ3JlZSwgTGluZ0V4cCwgRmlyc3RMYW5nLCAKICAgICAgICAgTG9jMV8xOkxvYzZfNSwgZXhwUHVycG9zZSkpICU+JQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikKCnF1ZXNkYXRhLnRleHQuc3ViIDwtIHF1ZXNkYXRhLnRleHQgJT4lIAogIHNlbGVjdCgtc3RhcnRzX3dpdGgoIlEiKSwgLVNDMCwgLXRvbmVzQ29ycmVjdCkgJT4lCiAgZHJvcGxldmVscygpCnF1ZXNkYXRhLnRleHQuc3ViCmBgYAoKIyMjIyBJSzI6IFdvcmQgU2VsZWN0aW9uIFF1ZXN0aW9uCklLMiAoSW1wbGljaXQgS25vd2xlZGdlIFEyKSByZWZlcnMgdG8gdGhlIHF1ZXN0aW9uIHdoZXJlIHN1YmplY3RzIHNlbGVjdCBhbGwgdGhlIHdvcmRzIHRoZXkgdGhpbmsgQ2FuYWRpYW5zIGFuZCBNaWNoaWdhbmRlcnMgd291bGQgcHJvbm91bmNlIGRpZmZlcmVudGx5ICgiV2hpY2ggb2YgdGhlIGZvbGxvd2luZyB3b3JkcywgaWYgYW55LCBkbyB5b3UgdGhpbmsgd291bGQgYmUgcHJvbm91bmNlZCBkaWZmZXJlbnRseSBieSBzb21lb25lIGZyb20gQ2FuYWRhLCBhcyBvcHBvc2VkIHRvIHNvbWVvbmUgZnJvbSBNaWNoaWdhbj8gUGxlYXNlIHNlbGVjdCBhbGwgdGhhdCBhcHBseS4iKS4gVGhlIHF1ZXN0aW9uIGluY2x1ZGVzIDMwIHdvcmRzLCA1IG9mIHdoaWNoIGFyZSB0YXJnZXQgL2F1LyB3b3JkcyBhbmQgNSBvZiB3aGljaCBhcmUgdGFyZ2V0IC9haS8gd29yZHMuCgpXZSB3YW50IHRvIGdvIGZyb20gdGhlIHJhdyBkYXRhLCB0aGUgc2VsZWN0ZWQgd29yZHMsIHRvIHRoZSBudW1iZXIgb2YgdGFyZ2V0IHdvcmRzICgvYXUvIGFuZCAvYWkvIHJhaXNpbmcgd29yZHMpIHNlbGVjdGVkLiBUaGUgcmVzcG9uc2UgZm9ybWF0IGlzIHdvcmRzIHNlcGFyYXRlZCBieSBjb21tYXMgaW4gb25lIGNlbGwuIFNvLCB3ZSBuZWVkIHRvIHNlcGFyYXRlIHRoZSBzdHJpbmdzIGludG8gc2VwYXJhdGUgd29yZHMsIGNoZWNrIHdoZXRoZXIgZWFjaCB0YXJnZXQgd29yZCBvY2N1cmVkLCB0aGVuIHRhYnVsYXRlIHRoZSBzY29yZXMuIEEgc2ltcGxlIHNlYXJjaCBmb3Igd29yZCBzdHJpbmdzIHdvbid0IHdvcmssIGJlY2F1c2Ugc29tZSB3b3JkcyAoZS5nLiBfb3V0XykgYXJlIHN1YnN0cmluZ3Mgb2Ygb3RoZXIgd29yZHMgKGUuZy4gX2Fib3V0XykuIAoKTXkgc29sdXRpb24gd2FzIHRvIGZpcnN0IHNlcGFyYXRlIHRoZSBzaW5nbGUgY29sdW1uIGJ5IGNvbW1hcyBpbnRvIG11bHRpcGxlIGNvbHVtbnMsIG9uZSBwZXIgd29yZC4gVG8gY2hlY2sgd2hldGhlciB0aGUgd29yZCB3YXMgYSByZXNwb25zZSwgSSBpbXBsZW1lbnQgYSBjb3VudCBvZiAxIGluIGEgbmV3IGNvbHVtbiBpZiBhIHNlYXJjaCBmdW5jdGlvbiBmaW5kcyB0aGUgdGFyZ2V0IHN0cmluZyBpbiBhIHJvdy4gVGhpcyBpcyBkb25lIGZvciBlYWNoIHRhcmdldCB3b3JkLiBGaW5hbGx5LCBJIHN1bSB0aGUgY291bnQgY29sdW1ucyBmb3IgL2FpLyBhbmQgL2F1LyBzZXBhcmF0ZWx5IHRvIGdldCBhIHNjb3JlIG91dCBvZiA1LgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMgSUsyOiBDYW4gV29yZCBTZWxlY3Rpb24gcXVlc3Rpb24KCiMgU2VsZWN0IG9ubHkgSUsyIHF1ZXN0aW9uICsgY3JlYXRlIGNvcGllZCBjb2x1bW4gb2YgV29yZHMgKGZvciBuZXh0IHN0ZXApCmlrMiA8LSBzZWxlY3QocXVlc2RhdGEudGV4dC5zdWIsIHBhcnRpY2lwYW50SWQsIElLMi5DYW5Xb3JkcykKaWsyIDwtIG11dGF0ZShpazIsIFdvcmRzID0gSUsyLkNhbldvcmRzKQoKIyBTZXBhcmF0ZSBXb3JkcyBpbnRvIGNvbHVtbnMgKGJ5IGNvbW1hKSArIGNyZWF0ZSBuZXcgY29sdW1ucyBvZiB3b3JkIGluIGxpc3QgcGVyIHJvdwojIE51bWJlciBvZiBjb2x1bW5zIG11c3QgYmUgdGhlIHNhbWUgZm9yIGV2ZXJ5IHJvdywgc28gZmluZCB0aGUgbWF4IG51bWJlciBvZiB3b3JkcyBzZWxlY3RlZCBieSBhbnkgc3ViamVjdCAoMjMgaGVyZSkKIyBFYWNoIHN1YmplY3Qgd2lsbCBoYXZlIDIzIGNvbHVtbnMsIG9uZSB3b3JkIHBlciBjb2x1bW4gKFdvcmRfMSwgV29yZF8yLi4uKSB1bnRpbCBubyBtb3JlIHdvcmRzIChOQSBpZiBubyB3b3JkKQppazIgPC0gc2VwYXJhdGUoaWsyLCAiV29yZHMiLCBwYXN0ZSgiV29yZCIsIDE6MzAsIHNlcD0iXyIpLCBzZXA9IiwiLCBleHRyYT0iZHJvcCIpCgojIElkZW50aWZ5IHRhcmdldCB3b3JkcyBpbiBlYWNoIHN1YmplY3RzJyByZXNwb25zZXMKIyBDb3VudCAxIGlmIHdvcmQgZXhpc3RzIGluIHJvdzsgMCBpZiBub25lCiMgL2F1LyB0YXJnZXRzCmlrMiA8LSBtdXRhdGUoaWsyLCBJSzIuYXUub3V0ID0gYXMuaW50ZWdlcihhcHBseShpazIsIDEsIGZ1bmN0aW9uKHgpIGFueSh4ICVpbiUgIm91dCIpKSksCiAgICAgICAgICAgICAgSUsyLmF1LmFib3V0ID0gYXMuaW50ZWdlcihncmVwbCgnYWJvdXQnLElLMi5DYW5Xb3JkcykpLAogICAgICAgICAgICAgIElLMi5hdS5kZXZvdXQgPSBhcy5pbnRlZ2VyKGdyZXBsKCdkZXZvdXQnLElLMi5DYW5Xb3JkcykpLAogICAgICAgICAgICAgIElLMi5hdS5ob3VzZSA9IGFzLmludGVnZXIoZ3JlcGwoJ2hvdXNlJyxJSzIuQ2FuV29yZHMpKSwKICAgICAgICAgICAgICBJSzIuYXUucG91Y2ggPSBhcy5pbnRlZ2VyKGdyZXBsKCdwb3VjaCcsSUsyLkNhbldvcmRzKSkpCgojIC9haS8gdGFyZ2V0cwppazIgPC0gbXV0YXRlKGlrMiwgSUsyLmFpLmxpa2UgPSBhcy5pbnRlZ2VyKGFwcGx5KGlrMiwgMSwgZnVuY3Rpb24oeCkgYW55KHggJWluJSAibGlrZSIpKSksCiAgICAgICAgICAgICAgSUsyLmFpLnJpZ2h0ID0gYXMuaW50ZWdlcihhcHBseShpazIsIDEsIGZ1bmN0aW9uKHgpIGFueSh4ICVpbiUgInJpZ2h0IikpKSwKICAgICAgICAgICAgICBJSzIuYWkubWlnaHQgPSBhcy5pbnRlZ2VyKGFwcGx5KGlrMiwgMSwgZnVuY3Rpb24oeCkgYW55KHggJWluJSAibWlnaHQiKSkpLAogICAgICAgICAgICAgIElLMi5haS51bml0ZSA9IGFzLmludGVnZXIoZ3JlcGwoJ3VuaXRlJyxJSzIuQ2FuV29yZHMpKSwKICAgICAgICAgICAgICBJSzIuYWkucmlwZSA9IGFzLmludGVnZXIoYXBwbHkoaWsyLCAxLCBmdW5jdGlvbih4KSBhbnkoeCAlaW4lICJyaXBlIikpKSkKCiMgU3VtIG9mIHRhcmdldHMgc2VsZWN0ZWQgZm9yIC9hdS8gYW5kIC9haS8KaWsyIDwtIG11dGF0ZShpazIsIElLMi5hdSA9IHJvd1N1bXMoc2VsZWN0KGlrMiwgSUsyLmF1Lm91dDpJSzIuYXUucG91Y2gpKSwKICAgICAgICAgICAgICBJSzIuYWkgPSByb3dTdW1zKHNlbGVjdChpazIsIElLMi5haS5saWtlOklLMi5haS5yaXBlKSkpCgojIEhlcmUgYXJlIHR3byB2ZXJzaW9ucyBvZiB0aGUgZGF0YQojIC4uLndpdGggc2NvcmUgZm9yIGVhY2ggdGFyZ2V0IHdvcmQgKyBzdW0gb2YgL2F1LyBhbmQgL2FpLyB0YXJnZXRzIHNlbGVjdGVkCmlrMi52YWx1ZXMgPC0gc2VsZWN0KGlrMiwgcGFydGljaXBhbnRJZCwgSUsyLmF1LCBJSzIuYWksIElLMi5hdS5vdXQ6SUsyLmFpLnJpcGUpCgojIC4uLndpdGggb25seSBzdW0gb2YgL2F1LyBhbmQgL2FpLyB0YXJnZXRzIHNlbGVjdGVkCmlrMi5zdW0gPC0gc2VsZWN0KGlrMiwgcGFydGljaXBhbnRJZCwgSUsyLmF1OklLMi5haSkKCmBgYAoKCgojIyMgUHJvY2VzcyBOdW0gUmVzcG9uc2VzCkEgZmV3IGNhc2VzIHdoZXJlIHBhcnRpY2lwYW50cyBkbyBub3QgcmVzcG9uZCB0byBhIHF1ZXN0aW9uIGJlY2F1c2UgdGhlIGFuc3dlciBpcyAnbm8nIHJlc3VsdHMgaW4gYW4gJ05BJyBlbnRyeS4gVGhlc2UgJ05BJ3MgZm9yIHNwZWNpZmljIGNvbHVtbnMgYXJlIGFkanVzdGVkICJtYW51YWxseSIgdG8gdGhlIGNvcnJlY3QgdmFsdWUuIAoKYGBge3IgbGJxZGF0YS1udW19CiMgU3Vic2V0IGRhdGEgdG8gbnVtIGZvcm1hdApxdWVzZGF0YS5udW0gPC0gcXVlc2RhdGEgJT4lIHN1YnNldChkYXRhRm9ybWF0ID09ICJudW0iKSAlPiUKICBzZWxlY3QocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQsIGd1aXNlQ29tYmluYXRpb24sIHNwZWFrZXJPcmRlciwgZXZlcnl0aGluZygpKSAlPiUKICBzZWxlY3QoLWMoQWdlLCBHZW5kZXIsIEV0aG5pY2l0eSwgU3BIRGlzb3JkZXIsIFNwSERpc29yZGVyXzJfVEVYVCwgRGVncmVlLCBMaW5nRXhwLCBGaXJzdExhbmcsIAogICAgICAgICBMb2MxXzE6TG9jNl81LCBleHBQdXJwb3NlKSkgJT4lCiAgcmVuYW1lKEVRLnJhd3MgPSBTQzApICU+JQogIG11dGF0ZV9hdCh2YXJzKHBhcnRpY2lwYW50SWQ6c3BlYWtlck9yZGVyKSwgYXMuZmFjdG9yKSAlPiUKICBtdXRhdGVfaWYofiBhbGwoZ3JlcGwoJ15cXGQrJCcsIC54KSksIGFzLm51bWVyaWMpCgojIyBGdXJ0aGVyIGFkanVzdG1lbnRzCnF1ZXNkYXRhLm51bS5zdWIgPC0gcXVlc2RhdGEubnVtICU+JQogICMgQ29udmVydCBjb2x1bW5zIHRvIG51bWVyaWMsIGxlYXZpbmcgbm9uLW51bWVyaWMgY29sdW1ucyBhcyBOQQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLm51bWVyaWMpICU+JQogICMgUmVtb3ZlIGNvbHVtbnMgdGhhdCBhcmUgYWxsIE5BIChzcGVjaWZpY2FsbHksIGlmIFN1bSBvZiB0aGF0IGNvbHVtbidzIE5BcyBpcyBOT1QgZXF1YWwgdG8gdGhlIG51bWJlciBvZiByb3dzLCBzZWxlY3QgKQogIHNlbGVjdF9pZihjb2xTdW1zKGlzLm5hKC4pKSAhPSBucm93KC4pKSAlPiUKICAjIFJlbW92ZSBjb2x1bW5zIG9mIGNlcnRhaW4gdHlwZXMgb2YgcXVlc3Rpb25zIG9yIHNwZWNpZmljIHF1ZXN0aW9uIG51bWJlcgogIHNlbGVjdCgtc3RhcnRzX3dpdGgoIlEiKSwgLXN0YXJ0c193aXRoKCJMYW5nIiksIC1zdGFydHNfd2l0aCgiSUsyIiksIC1zdGFydHNfd2l0aCgiTUUxIiksIC1lbmRzX3dpdGgoIlRFWFQiKSkgJT4lCgogICMgQWRqdXN0IHZhbHVlcyBvZiBOQSAoZm9yIGNhc2VzIHdoZXJlIHRoZXkgY2FuIGJlIGludGVycHJldGVkIGFzIGEgJ25vJyBvciAnbmV2ZXInKQogIG11dGF0ZShUcmF2ZWwuTkVfVmlzaXRzID0gY29hbGVzY2UoVHJhdmVsLk5FX1Zpc2l0cywgMSksIFRyYXZlbC5TX1Zpc2l0cyA9IGNvYWxlc2NlKFRyYXZlbC5TX1Zpc2l0cywgMSksCiAgICAgICAgIFRyYXZlbC5XX1Zpc2l0cyA9IGNvYWxlc2NlKFRyYXZlbC5XX1Zpc2l0cywgMSksIFRyYXZlbC5DYW5fVmlzaXRzID0gY29hbGVzY2UoVHJhdmVsLkNhbl9WaXNpdHMsIDEpLAogICAgICAgICBUcmF2ZWwuTkVfVGltZSA9IGNvYWxlc2NlKFRyYXZlbC5ORV9UaW1lLCAxKSwgVHJhdmVsLlNfVGltZSA9IGNvYWxlc2NlKFRyYXZlbC5TX1RpbWUsIDEpLAogICAgICAgICBUcmF2ZWwuV19UaW1lID0gY29hbGVzY2UoVHJhdmVsLldfVGltZSwgMSksIFRyYXZlbC5DYW5fVGltZSA9IGNvYWxlc2NlKFRyYXZlbC5DYW5fVGltZSwgMSksCiAgICAgICAgIFBFMS5SZWxhdGl2ZXMgPSBjb2FsZXNjZShQRTEuUmVsYXRpdmVzLCAyKSwgUEUxLkNsb3NlRmFtRnJpZW5kcyA9IGNvYWxlc2NlKFBFMS5DbG9zZUZhbUZyaWVuZHMsIDIpLAogICAgICAgICBFSzMuQ2FuQUkuRGlmZiA9IGNvYWxlc2NlKEVLMy5DYW5BSS5EaWZmLCAyKSwgRUs0LkNhbkFVLkRpZmYgPSBjb2FsZXNjZShFSzQuQ2FuQVUuRGlmZiwgMiksCiAgICAgICAgIE1FMy5Tb3VyY2VzLk90aGVyTWVkaWFfMSA9IGNvYWxlc2NlKE1FMy5Tb3VyY2VzLk90aGVyTWVkaWFfMSwgMSksIAogICAgICAgICBNRTMuU291cmNlcy5PdGhlcl8xID0gY29hbGVzY2UoTUUzLlNvdXJjZXMuT3RoZXJfMSwgMSksCiAgICAgICAgIE1FMy5Tb3VyY2VzLk5ld3NfMSA9IGNvYWxlc2NlKE1FMy5Tb3VyY2VzLk5ld3NfMSwgMSkpICU+JQogIGRyb3BsZXZlbHMoKQoKcXVlc2RhdGEubnVtLnN1YgpgYGAKCiMjIyBGaW5hbGl6ZSBDbGVhbmVkIFJlc3VsdHMKCmBgYHtyLCB3YXJuaW5nPUZ9CiMgU2VsZWN0IGFuZCBNZXJnZSBvbmx5IHJlbGF2YW50IG51bWVyaWNhbCBjb2x1bW5zIG9mIGRhdGEgZm9yIFBDQSBhbmFseXNpcwpxdWVzZGF0YS5jbGVhbiA8LSBxdWVzZGF0YS5kZW1vICU+JQogIHNlbGVjdChwYXJ0aWNpcGFudElkLCBjb25kaXRpb25JZCwgZ3Vpc2VDb21iaW5hdGlvbiwgc3BlYWtlck9yZGVyLCBBZ2UsIEdlbmRlciwgRXRobmljaXR5KSAlPiUKICBtZXJnZSguLCBpazIudmFsdWVzKSAlPiUKICBtZXJnZSguLCBxdWVzZGF0YS5udW0uc3ViKSAlPiUKICBkcm9wbGV2ZWxzKCkKCiMgcXVlc2RhdGEuZmluYWwKcXVlc2RhdGEuY2xlYW4KYGBgCgojIyBBbmFseXplIERhdGEKCiMjIyBQQ0E6IEF3YXJlbmVzcyAmIEV4cGVyaWVuY2UKCmBgYHtyfQojIENoZWNrIGNvcnJlbGF0aW9ucywgd2hpY2ggbW90aXZhdGUgdGhlIHVzZSBvZiBQQ0EgdG8gcmVkdWNlIGRpbWVuc2lvbmFsaXR5CndpdGgocXVlc2RhdGEuY2xlYW4sIGNvci50ZXN0KElLMi5haSwgSUsyLmF1KSkKCiMgVGVzdCBjb3JyZWxhdGlvbnMgb2Ygc2ltaWxhciBxdWVzdGlvbnMKd2l0aChxdWVzZGF0YS5jbGVhbiwgY29yLnRlc3QoVHJhdmVsLkNhbl9WaXNpdHMsIFRyYXZlbC5DYW5fVGltZSkpCndpdGgocXVlc2RhdGEuY2xlYW4sIGNvci50ZXN0KFNFMS5GYW0ub290XzEsIFNFMi5GcmVxLk92ZXJhbGxfMSkpCndpdGgocXVlc2RhdGEuY2xlYW4sIGNvci50ZXN0KFNFMi5GcmVxLk92ZXJhbGxfMSwgU0UyLkZyZXEuUmVjZW50XzEpKQp3aXRoKHF1ZXNkYXRhLmNsZWFuLCBjb3IudGVzdChTRTIuRnJlcS5PdmVyYWxsXzEsIFNFMi5GcmVxLkNoaWxkXzEpKQp3aXRoKHF1ZXNkYXRhLmNsZWFuLCBjb3IudGVzdChTRTIuRnJlcS5SZWNlbnRfMSwgU0UyLkZyZXEuQ2hpbGRfMSkpCmBgYAoKIyMjIyBSdW4gUENBCgpQYWNrYWdlcyByZXF1aXJlZCBmb3IgdGhpcyBQQ0E6CiogYFBDQWAgY29tbWFuZCBmcm9tIGBGYWN0b01pbmVSYCBsaWJyYXJ5IChzZWUgaW5kZXggZm9yIG1vcmUgaW5mbykKKiBgcGFyYW5gIGNvbW1hbmQgZnJvbSBgcGFyYW5gIGxpYnJhcnkKKiBgVmFyaW1heGAgY29tbWFuZCBmcm9tIGBHUEFyb3RhdGlvbnNgIGxpYnJhcnkgKGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzU5MjEzL2hvdy10by1jb21wdXRlLXZhcmltYXgtcm90YXRlZC1wcmluY2lwYWwtY29tcG9uZW50cy1pbi1yKQoKUGFja2FnZXMgZm9yIHZpc3VhbGl6YXRpb24gb2YgUENBCiogYGZ2aXpfcGNhX2luZGAgYW5kIGBmdml6X3BjYV9iaXBsb3RgIGZyb20gYGZhY3RvZXh0cmFgIAoKYGBge3IgbGJxZGF0YS1wY2EyLXByZXB9CiMgKDApIFNlbGVjdCBkYXRhIGZvciBQQ0Eg4oCUIG9ubHkgbnVtZXJpY2FsIGNvbHVtbnMKIyBuYW1lcyhxdWVzZGF0YS5jbGVhbikKCiMgTWVkaXVtIHRyaW1tZWQgc2V0IOKAlCByZW1vdmVzIGdlbmVyYWwgQ2FuYWRpYW4gc3RlcmVvdHlwZSBleHBlcmllbmNlL2tub3dsZWRnZSwgaW1pdGF0aW9uLCBTb3VyY2VzIG9mIENFLCBob2NrZXkKcXVlc2RhdGEucGNhLm1lZCA8LSBzZWxlY3QocXVlc2RhdGEuY2xlYW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICBJSzIuYXUsIElLMi5haSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFNFMS5GYW0ub290XzEsIFNFMi5GcmVxLlJlY2VudF8xOlNFMi5GcmVxLk92ZXJhbGxfMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFBFMi5DYW5TcGVha0ZyZXEuUmVjZW50XzE6UEUyLkNhblNwZWFrRnJlcS5PdmVyYWxsXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE1FNC5DYW5IZWFyRnJlcS5SZWNlbnRfMTpNRTQuQ2FuSGVhckZyZXEuT3ZlcmFsbF8xKQpxdWVzZGF0YS5wY2EubWVkCgpgYGAKCmBgYHtyfQojIyAoMSkgUnVuIFBhcmFsbGVsIEFuYWx5c2lzIHdpdGggYHBhcmFuYAojIFN0YW5kYXJkIHdheSB0byBkZWNpZGUgb24gdGhlIG51bWJlciBvZiBmYWN0b3JzIG9yIGNvbXBvbmVudHMgbmVlZGVkIGluIGFuIEZBIG9yIFBDQS4KIyBQcmludHMgb3V0IGEgc2NyZWUgcGxvdCBhcyB3ZWxsLCB3aXRoIHRoZSByYW5kb21pemVkIGxpbmUgKyB1bmFkanVzdGVkIGxpbmUKcGFyYW4ocXVlc2RhdGEucGNhLm1lZCwKICAgICAgZ3JhcGggPSBUUlVFLCBjb2xvciA9IFRSVUUsIAogICAgICBjb2wgPSBjKCJibGFjayIsICJyZWQiLCAiYmx1ZSIpLCBsdHkgPSBjKDEsIDIsIDMpLCBsd2QgPSAxLCBsZWdlbmQgPSBUUlVFLCAKICAgICAgZmlsZSA9ICIiLCB3aWR0aCA9IDY0MCwgaGVpZ2h0ID0gNjQwLCBncmRldmljZSA9ICJwbmciLCBzZWVkID0gMCkKYGBgCgpgYGB7ciwgd2FybmluZz1GfQojIyAoMikgUnVuIFBDQSB3aXRoIGBGYWN0b01pbmVSYAojIG5jcCA9IG51bWJlciBvZiBjb21wb25lbnRzOyBhZGp1c3QgYWZ0ZXIgY2hlY2tpbmcgdGhlIHBhcmFsbGVsIGFuYWx5c2lzIG91dHB1dAoKIyBGYWN0b01pbmVSIFBDQSBDb21tYW5kcwojcGxicVBDQSAgICAgICAgIyBsaXN0cyBjb21tYW5kcwojcGxicVBDQSR2YXIgICAgIyB2YXJpYWJsZXMKI3BsYnFQQ0EkaW5kICAgICMgaW5kaXZpZHVhbHMKI3BsYnFQQ0EkY2FsbCAgICMgc3VtbWFyeSBzdGF0cwoKbGJxUENBIDwtIFBDQShxdWVzZGF0YS5wY2EubWVkLCBzY2FsZS51bml0ID0gVCwgbmNwID0yLCBncmFwaD1GKQoKIyMgUmVsZXZhbnQgUmF3IFBDQSBPdXRwdXQKIyBFaWdlbnZhbHVlcyAmIHBlcmNlbnQgdmFyaWFuY2UgYWNjb3VudGVkIGZvcgooZWlnZW52YWx1ZXMgPC0gbGJxUENBJGVpZykKCiMgRWlnZW52ZWN0b3JzICg9RmFjdG9yIG1hdHJpeCwgZmFjdG9yIHNjb3JlIGNvZWZmaWNpZW50czsgc29tZXRpbWVzIGNhbGxlZCB0aGUgZmFjdG9yLCBidXQgTk9UIGZhY3RvciBzY29yZXMpCihlaWdlbnZlY3RvcnMgPC0gbGJxUENBJHZhciRjb29yZCkKCiMgRmFjdG9yIGxvYWRpbmdzIChlaWdlbnZlY3RvcnMgc2NhbGVkIGJ5IHRoZSBzcXVhcmUgcm9vdCBvZiB0aGVpciBhc3NvY2lhdGVkIGVpZ2VudmFsdWVzKQojIENhbGN1bGF0ZSBmYWN0b3IgbG9hZGluZ3MgdXNpbmcgdGhlIG91dHB1dCBlaWdlbnZlY3RvcnMgYW5kIGVpZ2VudmFsdWVzCnJhd0xvYWRpbmdzIDwtIHN3ZWVwKGxicVBDQSR2YXIkY29vcmQsMixzcXJ0KGxicVBDQSRlaWdbMTpuY29sKGxicVBDQSR2YXIkY29vcmQpLDFdKSxGVU49Ii8iKQpyYXdMb2FkaW5ncwoKIyBGYWN0b3Igc2NvcmVzIGZvciBlYWNoIHN1YmplY3QgYW5kIGRpbWVuc2lvbiAoYWxzbzogSW5kaXZpZHVhbCBjb29yZGluYXRlIHNjb3JlczsgcHJpbmNpcGxlIGNvb3JkaW5hdGVzKQpyYXdTY29yZXMgPC0gbGJxUENBJGluZCRjb29yZAoKYGBgCgpgYGB7cn0KIyMgKDMpIENvbmR1Y3Qgcm90YXRpb24gb24gdGhlIFBDQSBmYWN0b3IgbG9hZGluZ3Mgd2l0aCBgR1BBcm90YXRpb25gCiMgUm90YXRpb25zIGFyZSB0eXBpY2FsbHkgZG9uZSBvbiB0aGUgcmV0YWluZWQgY29tcG9uZW50IGZhY3RvciBsb2FkaW5ncywgbm90IG9uIGFsbCBjb21wb25lbnRzIG5vciBvbiB0aGUgZWlnZW52ZWN0b3JzCiMgUGVyZm9ybWVkIGZvciBlYXNlIG9mIGludGVycHJldGF0aW9uLCBtYXhpbWl6aW5nIGZhY3RvciBsb2FkaW5ncwoocm90TG9hZGluZ3MgPC0gVmFyaW1heChyYXdMb2FkaW5ncykkbG9hZGluZ3MpCgojIFJlY292ZXIgUm90YXRpb24gbWF0cml4IGZyb20gbG9hZGluZ3MKIyBCZWNhdXNlIHRoZSByb3RMb2FkaW5ncyBhcmUgY2FsY3VsYXRlZCBmcm9tIHJhd0xvYWRpbmdzICUqJSByb3RNYXRyaXgsIGNhbiByZWNvdmVyIHJvdE1hdHJpeCBieSByb3RMb2FkaW5ncyAiZGl2aWRlZCIgYnkgcmF3TG9hZGluZ3MsIHdoaWNoIGluIG1hdHJpeCBtdWx0aXBsaWNhdGlvbiBpcyBtdWx0aXBseWluZyBieSB0aGUgaW52ZXJzZSAodHJhbnNwb3NlKSAKIyBOb3RlOiBGb3Igc29tZSByZWFzb24sIGNhbid0IGNhbGwgVmFyaW1heChyYXdMb2FkaW5ncykkcm90bWF0IChqdXN0IGdldCBOVUxMKTsgdGhpcyByZWNyZWF0ZXMgdGhlIHNhbWUgbWF0cml4IGZyb20gVmFyaW1heChyYXdMb2FkaW5ncykKKHJvdE1hdHJpeEwgPC0gdChyYXdMb2FkaW5ncykgJSolIHJvdExvYWRpbmdzKQoKIyBDYWxjdWxhdGUgcm90YXRlZCBmYWN0b3Igc2NvcmVzCiMgVGhlIGZvcm11bGEgc2ltcGx5IG11bHRpcGxpZXMgdGhlIG5vcm1hbGl6ZWQgdmFyaWFibGUgc2NvcmVzIHdpdGggdGhlIHJvdGF0aW9uIG1hdHJpeCB0byBnZXQgcm90YXRlZCBmYWN0b3Igc2NvcmVzCiMgRmlyc3QsIHotc2NvcmUgdGhlIHJhdyBzY29yZXMgdXNpbmcgYmFzZSBSIHNjYWxlKCkKIyBUaGVuLCBtYXRyaXggbXVsdGlwbHkgdGhlIG1hdHJpeCBvZiB6U2NvcmVzIHdpdGggdGhlIHJvdGF0aW9uIG1hdHJpeAojIFJlc3VsdCBpcyBhIG1hdHJpeCB3aXRoIGNvbHVtbnM9Y29tcG9uZW50cyBhbmQgcm93cz1lYWNoIHN1YmplY3QKelNjb3JlcyA8LSBzY2FsZShyYXdTY29yZXMpCnJvdFNjb3JlcyA8LSB6U2NvcmVzICUqJSByb3RNYXRyaXhMCmBgYAoKIyMjIyBQbG90IFBDQQpgYGB7cn0KIyMgKDQpIERhdGEgVmlzdWFsaXphdGlvbiBvZiBSYXcgU2NvcmVzIHdpdGggYGZhY3RvZXh0cmFgCgojIFBsb3QgaW5kaXZpZHVhbCBmYWN0b3Igc2NvcmVzCmZ2aXpfcGNhX2luZChsYnFQQ0EsIGNvbC5pbmQgPSAiIzAwQUZCQiIsIHJlcGVsID0gVFJVRSkKCiMgQmlwbG90LCBpbmNsdWRpbmcgaW5kaXZpZHVhbCBzY29yZXMgYW5kIGZhY3RvciB2ZWN0b3JzCmZ2aXpfcGNhX2JpcGxvdChsYnFQQ0EsIGxhYmVsID0gImFsbCIsIGNvbC5pbmQgPSAiIzAwQUZCQiIsIGNvbC52YXI9ImJsYWNrIiwgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSkKYGBgCgpgYGB7cn0KIyMgKDUpIE1hbnVhbCBQbG90cyBvZiBSb3RhdGVkIFNjb3JlcyB3aXRoIGBnZ3Bsb3RgCgojIyBDcmVhdGUgZGF0YWZyYW1lcyBvZiB0aGUgcm90YXRlZCBmYWN0b3IgbG9hZGluZyBhbmQgZmFjdG9yIHNjb3JlIG1hdHJpY2VzCiMgQ29udmVydCByb3RhdGVkIGZhY3RvciBsb2FkaW5ncyBtYXRyaXggdG8gZGF0YSBmcmFtZTsgYWRkIHZhcmlhYmxlIG51bWJlcgpyb3RMb2FkaW5nc0RhdGEgPC0gYXMuZGF0YS5mcmFtZShyb3RMb2FkaW5ncykKcm90TG9hZGluZ3NEYXRhIDwtIG11dGF0ZShyb3RMb2FkaW5nc0RhdGEsIHZhcmlhYmxlID0gcm93Lm5hbWVzKHJvdExvYWRpbmdzKSkKcm90TG9hZGluZ3NEYXRhIDwtIG11dGF0ZShyb3RMb2FkaW5nc0RhdGEsIHZhcmlhYmxlID0gZmFjdG9yKHZhcmlhYmxlKSkKCiMgQ29udmVydCByb3RhdGVkIGZhY3RvciBzY29yZSBtYXRyaXggdG8gZGF0YSBmcmFtZTsgYWRkIHN1YmplY3QgbnVtYmVyCnJvdFNjb3JlRGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJvdFNjb3JlcykKcm90U2NvcmVEYXRhIDwtIHJvdFNjb3JlRGF0YSAlPiUgbXV0YXRlKHN1YmplY3QgPSAxOm5yb3coLikpCgojIyBDcmVhdGUgYmFzZSBwbG90cwojIExvYWRpbmcgcGxvdApsb2FkaW5ncGxvdCA8LSByb3RMb2FkaW5nc0RhdGEgJT4lIGdncGxvdChhZXMoeD1EaW0uMSwgeT1EaW0uMikpKwogIGdlb21fc2VnbWVudChkYXRhPXJvdExvYWRpbmdzRGF0YSwgbWFwcGluZz1hZXMoeD0wLCB5PTAsIHhlbmQ9RGltLjEqNCwgeWVuZD1EaW0uMio0KSwgYXJyb3c9YXJyb3coKSwgc2l6ZT0wLjUsIGNvbG9yPSJibGFjayIpICsKICBnZW9tX3RleHQoZGF0YT1yb3RMb2FkaW5nc0RhdGEsIGFlcyh4PURpbS4xKjQsIHk9RGltLjIqNCwgbGFiZWw9dmFyaWFibGUpLCBjb2xvcj0icmVkIixjaGVja19vdmVybGFwPVQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltPWMoLTIuNzUsIDIuNzUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW09YygtMy41LCAzLjUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGxhYnModGl0bGU9IlZhcmlhYmxlcyAtIFBDQSIsIHg9IkRpbSAxICgzNi40JSkiLCB5PSJEaW0gMiAoMTcuNyUpIikgKwogIGdnX3RoZW1lKCkKbG9hZGluZ3Bsb3QKCiMgU2NhdHRlciBwbG90IG9mIEluZGl2aWR1YWwgZmFjdG9yIHNjb3JlcwpkaW1wbG90ID0gZ2dwbG90KHJvdFNjb3JlRGF0YSwgYWVzKHg9RGltLjEsIHk9RGltLjIpKSsKICBnZW9tX3BvaW50KG5hLnJtPVRSVUUsIGNvbG9yPSIjMDBBRkJCIikgKwogIGdlb21fdGV4dChhZXMobGFiZWw9c3ViamVjdCksaGp1c3Q9MS41LHZqdXN0PTEuNSwgY29sb3I9IiMwMEFGQkIiLCBjaGVja19vdmVybGFwPVQpKwogIHNjYWxlX3hfY29udGludW91cyhsaW09YygtMi43NSwgMi43NSksYnJlYWtzPXNlcSgtMywzLDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbT1jKC0zLjUsIDMuNSksYnJlYWtzPXNlcSgtMywzLDEpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkYXNoZWQiKSArCiAgbGFicyh0aXRsZT0iSW5kaXZpZHVhbHMgLSBQQ0EiLCB4PSJEaW0gMSAoMzYuNCUpIiwgeT0iRGltIDIgKDE3LjclKSIpICsKICBnZ190aGVtZSgpCmRpbXBsb3QKCiMjIE1lcmdlIGxvYWRpbmcgYW5kIHNjb3JlIHBsb3QgPSBCaXBsb3QKCiMgQmlwbG90IG9mIGZhY3RvciBsb2FkaW5ncyArIGluZCBmYWN0b3Igc2NvcmVzCmdncGxvdChyb3RTY29yZURhdGEsIGFlcyh4PURpbS4xLCB5PURpbS4yKSkrCiAgZ2VvbV9wb2ludChuYS5ybT1UUlVFLCBjb2xvcj0iIzAwQUZCQiIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXN1YmplY3QpLGhqdXN0PTEuNSx2anVzdD0xLjUsIGNvbG9yPSIjMDBBRkJCIiwgY2hlY2tfb3ZlcmxhcD1UKSsKICAKICAjIE92ZXJsYXkgbG9hZGluZyBwbG90IChpLmUuIGFycm93cykKICBnZW9tX3NlZ21lbnQoZGF0YT1yb3RMb2FkaW5nc0RhdGEsIG1hcHBpbmc9YWVzKHg9MCwgeT0wLCB4ZW5kPURpbS4xKjQsIHllbmQ9RGltLjIqNCksIGFycm93PWFycm93KCksIHNpemU9MC41LCBjb2xvcj0iYmxhY2siKSArCiAgZ2VvbV90ZXh0KGRhdGE9cm90TG9hZGluZ3NEYXRhLCBhZXMoeD1EaW0uMSo0LjUsIHk9RGltLjIqNC41LCBsYWJlbD12YXJpYWJsZSksIGNvbG9yPSJyZWQiLGNoZWNrX292ZXJsYXA9VCwgbnVkZ2VfeSA9IDApKwoKICBzY2FsZV94X2NvbnRpbnVvdXMobGltPWMoLTIuNzUsIDIuNzUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW09YygtMy41LCAzLjUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGxhYnModGl0bGU9IkJpcGxvdCAtIFBDQSIsIHg9IkRpbSAxICgzNi40JSkiLCB5PSJEaW0gMiAoMTcuNyUpIikgKwogIGdnX3RoZW1lKCkKCmBgYAoKIyMjIyBJbnRlcnByZXQgUENBCmBgYHtyfQojIyBJbnRlcnByZXQgUENzIChEaW1lbnNpb25zKSBiYXNlZCBvbiBmYWN0b3IgbG9hZGluZ3MKcm90TG9hZGluZ3MuZGYgPC0gYXMuZGF0YS5mcmFtZShyb3RMb2FkaW5ncykgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKC4sICJWYXJpYWJsZXMiKSAlPiUKICByZW5hbWUoLiwgIlBDMSI9ICJEaW0uMSIsICJQQzIiID0gIkRpbS4yIikKcm90TG9hZGluZ3MuZGYKYGBgCgpgYGB7cn0KIyBQQzEgT25seSBDb250cmlidXRvcnMKcm90TG9hZGluZ3MuZGYgJT4lIGZpbHRlcihhYnMoUEMxKSA+IDAuMikKYGBgCgoKYGBge3J9CiMgUEMyIE9ubHkgQ29udHJpYnV0b3JzCnJvdExvYWRpbmdzLmRmICU+JSBmaWx0ZXIoYWJzKFBDMikgPiAwLjIpCmBgYAoKYGBge3IsIHdhcm5pbmc9Rn0KIyBDaGVjayBmb3Igb3ZlcmxhcHBpbmcgY29udHJpYnV0b3JzCnJvdExvYWRpbmdzLmRmICU+JSBmaWx0ZXIoYWJzKFBDMikgPiAwLjIgJiBhYnMoUEMxKSA+IDAuMikKCiMgQ2hlY2sgZm9yIG5vbi1jb250cmlidXRvcnMKcm90TG9hZGluZ3MuZGYgJT4lIGZpbHRlcihhYnMoUEMyKSA8IDAuMiAmIGFicyhQQzEpIDwgMC4yKQpgYGAKCgoKIyMjIEZpbmFsaXplIFJlc3VsdHMgdy8gUENBCgpgYGB7cn0KIyBGb3IgTWVyZ2luZzogQ29udmVydCByb3RhdGVkIGZhY3RvciBzY29yZSBtYXRyaXggdG8gZGF0YSBmcmFtZTsgYWRkIHBhcnRpY2lwYW50SWQgKGFzc3VtaW5nIG9yZGVyIG9mIGlucHV0IGRhdGFmcmFtZSkKaW5kUENBZGF0YSA8LSByb3RTY29yZURhdGEgJT4lCiAgbXV0YXRlKHBhcnRpY2lwYW50SWQgPSBxdWVzZGF0YS5jbGVhbiRwYXJ0aWNpcGFudElkKSAlPiUKICByZW5hbWUoQ0VzY29yZSA9IERpbS4xKSAlPiUKICByZW5hbWUoU0FzY29yZSA9IERpbS4yKQoKIyBNZXJnZSBwYXJ0aWNpcGFudCBkYXRhIHdpdGggUEMgc2NvcmVzIAojIE9ubHkgc2VsZWN0IHRoZSBtYWluIHJlbGV2YW50IHNjb3JlcwpxdWVzZGF0YS5maW5hbCA8LSBxdWVzZGF0YS5jbGVhbiAlPiUKICBtZXJnZSguLCBpbmRQQ0FkYXRhKSAlPiUKICBtdXRhdGUoTVNzY29yZSA9IHNjYWxlKFNLMi5NaWNodlN0YW5kKSkgJT4lCiAgbXV0YXRlKEVRc2NvcmUgPSBzY2FsZShFUS5yYXdzKSkgJT4lCiAgbXV0YXRlX2F0KHZhcnMoQWdlKSwgYXMubnVtZXJpYykgJT4lCiAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMuZmFjdG9yKSAlPiUKICBzZWxlY3QocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQsIGd1aXNlQ29tYmluYXRpb24sIHNwZWFrZXJPcmRlciwgQWdlLCBHZW5kZXIsIEV0aG5pY2l0eSwgQ0VzY29yZSwgU0FzY29yZSwgTVNzY29yZSwgRVFzY29yZSwgRVEucmF3cywgZXZlcnl0aGluZygpKQoKc3VtbWFyeShxdWVzZGF0YS5maW5hbCkKCnF1ZXNkYXRhLmZpbmFsCgojIFdyaXRlIHRvIGZpbGUKd3JpdGUuY3N2KHF1ZXNkYXRhLmZpbmFsLCAnZGF0YS9heGJfMWJfbGJxX2RhdGEuY3N2Jywgcm93Lm5hbWVzPUYpCgpgYGAKCiMjIFN1bW1hcml6ZSBEYXRhCiMjIyBUYWJsZXMKIyMjIyBBbGwgU2NvcmVzCmBgYHtyfQojIFRvdGFsCnF1ZXNkYXRhLmZpbmFsICU+JSBzZWxlY3QocGFydGljaXBhbnRJZCwgY29uZGl0aW9uSWQsIGd1aXNlQ29tYmluYXRpb24sIEFnZSwgR2VuZGVyLCBFdGhuaWNpdHksIENFc2NvcmUsIFNBc2NvcmUsIE1Tc2NvcmUsIEVRc2NvcmUsIEVRLnJhd3MpICU+JQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkLCBtaW4gPSBtaW4sIG1heCA9IG1heCkpICU+JSAKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9IGMoIk1lYXN1cmUiLCAiU3RhdCIpLCBuYW1lc19zZXAgPSAiXyIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQogIHBpdm90X3dpZGVyKE1lYXN1cmUsIG5hbWVzX2Zyb20gPSAiU3RhdCIsIHZhbHVlc19mcm9tID0gInZhbHVlIikgJT4lCiAgbXV0YXRlKG1lYW4gPSByb3VuZChtZWFuLCBkaWdpdHMgPSA2KSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gIlRvdGFsIikgJT4lCiAgc2VsZWN0KGdyb3VwLCBldmVyeXRoaW5nKCkpCgpgYGAKCgpgYGB7cn0KIyBCYXNlbGluZQpxdWVzZGF0YS5maW5hbCAlPiUgc2VsZWN0KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkLCBndWlzZUNvbWJpbmF0aW9uLCBBZ2UsIEdlbmRlciwgRXRobmljaXR5LCBDRXNjb3JlLCBTQXNjb3JlLCBNU3Njb3JlLCBFUXNjb3JlLCBFUS5yYXdzKSAlPiUKICBmaWx0ZXIoZ3Vpc2VDb21iaW5hdGlvbiA9PSAiYmFzZWxpbmUiKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCwgbWluID0gbWluLCBtYXggPSBtYXgpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSBjKCJNZWFzdXJlIiwgIlN0YXQiKSwgbmFtZXNfc2VwID0gIl8iLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICBwaXZvdF93aWRlcihNZWFzdXJlLCBuYW1lc19mcm9tID0gIlN0YXQiLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpICU+JQogIG11dGF0ZShtZWFuID0gcm91bmQobWVhbiwgZGlnaXRzID0gNikpICU+JQogIG11dGF0ZShncm91cCA9ICJiYXNlbGluZSIpICU+JQogIHNlbGVjdChncm91cCwgZXZlcnl0aGluZygpKQoKIyBNYXRjaApxdWVzZGF0YS5maW5hbCAlPiUgc2VsZWN0KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkLCBndWlzZUNvbWJpbmF0aW9uLCBBZ2UsIEdlbmRlciwgRXRobmljaXR5LCBDRXNjb3JlLCBTQXNjb3JlLCBNU3Njb3JlLCBFUXNjb3JlLCBFUS5yYXdzKSAlPiUKICBmaWx0ZXIoZ3Vpc2VDb21iaW5hdGlvbiA9PSAibWF0Y2giKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCwgbWluID0gbWluLCBtYXggPSBtYXgpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSBjKCJNZWFzdXJlIiwgIlN0YXQiKSwgbmFtZXNfc2VwID0gIl8iLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICBwaXZvdF93aWRlcihNZWFzdXJlLCBuYW1lc19mcm9tID0gIlN0YXQiLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpICU+JQogIG11dGF0ZShtZWFuID0gcm91bmQobWVhbiwgZGlnaXRzID0gNikpICU+JQogIG11dGF0ZShncm91cCA9ICJtYXRjaCIpICU+JQogIHNlbGVjdChncm91cCwgZXZlcnl0aGluZygpKQoKIyBNaXNtYXRjaApxdWVzZGF0YS5maW5hbCAlPiUgc2VsZWN0KHBhcnRpY2lwYW50SWQsIGNvbmRpdGlvbklkLCBndWlzZUNvbWJpbmF0aW9uLCBBZ2UsIEdlbmRlciwgRXRobmljaXR5LCBDRXNjb3JlLCBTQXNjb3JlLCBNU3Njb3JlLCBFUXNjb3JlLCBFUS5yYXdzKSAlPiUKICBmaWx0ZXIoZ3Vpc2VDb21iaW5hdGlvbiA9PSAibWlzbWF0Y2giKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCwgbWluID0gbWluLCBtYXggPSBtYXgpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSBjKCJNZWFzdXJlIiwgIlN0YXQiKSwgbmFtZXNfc2VwID0gIl8iLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICBwaXZvdF93aWRlcihNZWFzdXJlLCBuYW1lc19mcm9tID0gIlN0YXQiLCB2YWx1ZXNfZnJvbSA9ICJ2YWx1ZSIpICU+JQogIG11dGF0ZShtZWFuID0gcm91bmQobWVhbiwgZGlnaXRzID0gNikpICU+JQogIG11dGF0ZShncm91cCA9ICJtaXNtYXRjaCIpICU+JQogIHNlbGVjdChncm91cCwgZXZlcnl0aGluZygpKQpgYGAKCiMjIyMgUENBIENvbnRyaWJ1dG9ycwpgYGB7cn0KI3F1ZXNkYXRhLmZpbmFsLnN1bSA8LSAKICBxdWVzZGF0YS5maW5hbCAlPiUgZ3JvdXBfYnkoZ3Vpc2VDb21iaW5hdGlvbikgJT4lCiAgc3VtbWFyaXplKCNtZWFuRVEgPSBtZWFuKEVRc2NvcmUpLCAKICAgICAgICAgICAgbWVhbklLMi5hdSA9IG1lYW4oSUsyLmF1KSwgbWVhbklLMi5haSA9IG1lYW4oSUsyLmFpKSwgCiAgICAgICAgICAgICNtZWFuRGVjaWRlQ2FuID0gbWVhbihJSzEuRGVjaWRlQ2FuYWRhKSwgCiAgICAgICAgICAgICNtZWFuRWhGYW0gPSBtZWFuKFNFMS5GYW0uZWhfMSksIAogICAgICAgICAgICBtZWFuT290RmFtID0gbWVhbihTRTEuRmFtLm9vdF8xKSwgCiAgICAgICAgICAgIG1lYW5TRUZyZXEgPSBtZWFuKFNFMi5GcmVxLk92ZXJhbGxfMSksIG1lYW5TRUZyZXEuQyA9IG1lYW4oU0UyLkZyZXEuQ2hpbGRfMSksIAogICAgICAgICAgICBtZWFuU0VGcmVxLlIgPSBtZWFuKFNFMi5GcmVxLlJlY2VudF8xKSwgCiAgICAgICAgICAgIG1lYW5TRUFjYyA9IG1lYW4oU0UzLkFjY3VyYWN5KSkKCiNxdWVzZGF0YS5maW5hbC5zdW0gPC0gCiAgcXVlc2RhdGEuZmluYWwgJT4lIGdyb3VwX2J5KGd1aXNlQ29tYmluYXRpb24pICU+JQogIHN1bW1hcml6ZShDYW5IZWFyRnJlcSA9IG1lYW4oTUU0LkNhbkhlYXJGcmVxLk92ZXJhbGxfMSksIAogICAgICAgICAgICBDYW5IZWFyRnJlcS5SID0gbWVhbihNRTQuQ2FuSGVhckZyZXEuUmVjZW50XzEpLAogICAgICAgICAgICBDYW5IZWFyRnJlcS5DID0gbWVhbihNRTQuQ2FuSGVhckZyZXEuQ2hpbGRfMSksCiAgICAgICAgICAgIENhblNwZWFrRnJlcSA9IG1lYW4oUEUyLkNhblNwZWFrRnJlcS5PdmVyYWxsXzEpLAogICAgICAgICAgICBDYW5TcGVha0ZyZXEuUiA9IG1lYW4oUEUyLkNhblNwZWFrRnJlcS5SZWNlbnRfMSksCiAgICAgICAgICAgIENhblNwZWFrRnJlcS5DID0gbWVhbihQRTIuQ2FuU3BlYWtGcmVxLkNoaWxkXzEpKQpgYGAKCiMjIyMgWWVzL05vIFJlc3BvbnNlcwpgYGB7cn0KcXVlc2RhdGEuZmluYWwgJT4lIGdyb3VwX2J5KGd1aXNlQ29tYmluYXRpb24pICU+JSBtdXRhdGUoRUsxLkNhblNwZWFrID0gaWZlbHNlKEVLMS5DYW5TcGVhaz09MSwgInllcyIsICJubyIpKSAlPiUKY291bnQoRUsxLkNhblNwZWFrKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRUsxLkNhblNwZWFrLCB2YWx1ZXNfZnJvbSA9IG4pICU+JQogIG11dGF0ZShxdWVzdGlvbj0iU3BlYWtEaWZmIikgJT4lIHJlbG9jYXRlKHF1ZXN0aW9uKQoKcXVlc2RhdGEuZmluYWwgJT4lIGdyb3VwX2J5KGd1aXNlQ29tYmluYXRpb24pICU+JSBtdXRhdGUoRUsyLkNhblByb24gPSBpZmVsc2UoRUsyLkNhblByb249PTEsICJ5ZXMiLCAibm8iKSkgJT4lCmNvdW50KEVLMi5DYW5Qcm9uKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRUsyLkNhblByb24sIHZhbHVlc19mcm9tID0gbikgICU+JQogIG11dGF0ZShxdWVzdGlvbj0iUHJvbm91bmNlRGlmZiIpICU+JSByZWxvY2F0ZShxdWVzdGlvbikKCnF1ZXNkYXRhLmZpbmFsICU+JSBncm91cF9ieShndWlzZUNvbWJpbmF0aW9uKSAlPiUgbXV0YXRlKEVLMy5DYW5BSSA9IGlmZWxzZShFSzMuQ2FuQUk9PTEsICJ5ZXMiLCAibm8iKSkgJT4lCmNvdW50KEVLMy5DYW5BSSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVLMy5DYW5BSSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUKICBtdXRhdGUocHJvcC55ZXMgPSB5ZXMvKHllcytubykpICAlPiUKICBtdXRhdGUocXVlc3Rpb249ImFpRGlmZiIpICU+JSByZWxvY2F0ZShxdWVzdGlvbikKCnF1ZXNkYXRhLmZpbmFsICU+JSBncm91cF9ieShndWlzZUNvbWJpbmF0aW9uKSAlPiUgbXV0YXRlKEVLNC5DYW5BVSA9IGlmZWxzZShFSzQuQ2FuQVU9PTEsICJ5ZXMiLCAibm8iKSkgJT4lCmNvdW50KEVLNC5DYW5BVSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVLNC5DYW5BVSwgdmFsdWVzX2Zyb20gPSBuKSAgJT4lCiAgbXV0YXRlKHByb3AueWVzID0geWVzLyh5ZXMrbm8pKSAgJT4lCiAgbXV0YXRlKHF1ZXN0aW9uPSJhdURpZmYiKSAlPiUgcmVsb2NhdGUocXVlc3Rpb24pCmBgYAoKCmBgYHtyfQojIE92ZXJhbGwgdG90YWwKcXVlc2RhdGEuZmluYWwgJT4lIG11dGF0ZShFSzMuQ2FuQUkgPSBpZmVsc2UoRUszLkNhbkFJPT0xLCAieWVzIiwgIm5vIikpICU+JSBjb3VudChFSzMuQ2FuQUkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFSzMuQ2FuQUksIHZhbHVlc19mcm9tID0gbikgJT4lIG11dGF0ZShwcm9wLnllcyA9IHllcy8oeWVzK25vKSkgICU+JQogIG11dGF0ZShxdWVzdGlvbj0iYWlEaWZmIikgJT4lIHJlbG9jYXRlKHF1ZXN0aW9uKSAlPiUKICByYmluZCguLHF1ZXNkYXRhLmZpbmFsICU+JSBtdXRhdGUoRUs0LkNhbkFVID0gaWZlbHNlKEVLNC5DYW5BVT09MSwgInllcyIsICJubyIpKSAlPiUgY291bnQoRUs0LkNhbkFVKSAlPiUKICAgICAgICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFSzQuQ2FuQVUsIHZhbHVlc19mcm9tID0gbiklPiUgbXV0YXRlKHByb3AueWVzID0geWVzLyh5ZXMrbm8pKSAgJT4lCiAgICAgICAgICBtdXRhdGUocXVlc3Rpb249ImF1RGlmZiIpICU+JSByZWxvY2F0ZShxdWVzdGlvbikKICApCgpgYGAKCiMjIyBQbG90cwojIyMjIEVRIFNjb3JlcwpgYGB7cn0KIyBEZW5zaXR5IFBsb3Qgb2Ygc2NvcmUgZGlzdHJpYnV0aW9ucwpxdWVzZGF0YS5maW5hbCAlPiUgbXV0YXRlKGd1aXNlQ29tYmluYXRpb24gPSBwbHlyOjptYXB2YWx1ZXMoZ3Vpc2VDb21iaW5hdGlvbiwgZnJvbSA9IGMoImJhc2VsaW5lIiwgIm1hdGNoIiwgIm1pc21hdGNoIiksIHRvID0gYygiTm8tR3Vpc2UiLCAiR3Vpc2UtTWF0Y2giLCAiR3Vpc2UtTWlzbWF0Y2giKSkpICU+JQogIGdncGxvdChhZXMoeD1FUXNjb3JlLGZpbGw9Z3Vpc2VDb21iaW5hdGlvbixjb2xvcj1ndWlzZUNvbWJpbmF0aW9uKSkrCiAgIGdlb21fZGVuc2l0eShhbHBoYT0wLjMpICsKICBsYWJzKHRpdGxlPSIiLCB4PSJFUSBTY29yZSIsIHk9IkNvdW50IikgKwogIGdnX3RoZW1lKCkKCmBgYApgYGB7cn0KIyBCeSBHZW5kZXIKZ2dwbG90KGRhdGE9cXVlc2RhdGEuZmluYWwsIGFlcyh4PUdlbmRlciwgeT1FUXNjb3JlKSkgKwogIGdlb21fYm94cGxvdChhZXMobGluZXR5cGUgPSBHZW5kZXIpLCBmaWxsPSJncmV5IiwgYWxwaGE9MC4zLCBuYS5ybT1UUlVFKSArCiAgZ2dfdGhlbWUoKQoKIyBCeSBHZW5kZXIgKyBHdWlzZQpnZ3Bsb3QoZGF0YT1xdWVzZGF0YS5maW5hbCwgYWVzKHg9R2VuZGVyLCB5PUVRc2NvcmUpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gZ3Vpc2VDb21iaW5hdGlvbiwgY29sb3I9Z3Vpc2VDb21iaW5hdGlvbiwgbGluZXR5cGU9R2VuZGVyKSwgYWxwaGE9MC4zLCBuYS5ybT1UUlVFKSArCiAgZmFjZXRfZ3JpZCh+Z3Vpc2VDb21iaW5hdGlvbikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDMsNCldKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNiwzLDQpXSkgKwogIGdnX3RoZW1lKCkKCiMgQnkgR3Vpc2UKZ2dwbG90KGRhdGE9cXVlc2RhdGEuZmluYWwsIGFlcyh4PWd1aXNlQ29tYmluYXRpb24sIHk9RVFzY29yZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBndWlzZUNvbWJpbmF0aW9uLCBjb2xvcj1ndWlzZUNvbWJpbmF0aW9uKSwgYWxwaGE9MC4zLCBuYS5ybT1UUlVFKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1naGlibGlfcGFsZXR0ZSgiUG9ueW9NZWRpdW0iKVtjKDYsMyw0KV0pICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDMsNCldKSArCiAgZ2dfdGhlbWUoKQoKYGBgCgojIyMjIFNBIFNjb3JlcwoKYGBge3J9CiMgRGVuc2l0eSBQbG90IG9mIHNjb3JlIGRpc3RyaWJ1dGlvbnMKcXVlc2RhdGEuZmluYWwgJT4lIG11dGF0ZShndWlzZUNvbWJpbmF0aW9uID0gcGx5cjo6bWFwdmFsdWVzKGd1aXNlQ29tYmluYXRpb24sIGZyb20gPSBjKCJiYXNlbGluZSIsICJtYXRjaCIsICJtaXNtYXRjaCIpLCB0byA9IGMoIk5vLUd1aXNlIiwgIkd1aXNlLU1hdGNoIiwgIkd1aXNlLU1pc21hdGNoIikpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9U0FzY29yZSxmaWxsPWd1aXNlQ29tYmluYXRpb24sY29sb3I9Z3Vpc2VDb21iaW5hdGlvbikpKwogICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4zKSArCiAgbGFicyh0aXRsZT0iIiwgeD0iU0EgU2NvcmUiLCB5PSJDb3VudCIpICsKICBnZ190aGVtZSgpCgpgYGAKCmBgYHtyfQojIEJ5IEdlbmRlcgpnZ3Bsb3QoZGF0YT1xdWVzZGF0YS5maW5hbCwgYWVzKHg9R2VuZGVyLCB5PVNBc2NvcmUpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhsaW5ldHlwZSA9IEdlbmRlciksIGZpbGw9ImdyZXkiLCBhbHBoYT0wLjMsIG5hLnJtPVRSVUUpICsKICBnZ190aGVtZSgpCgojIEJ5IEdlbmRlciArIEd1aXNlCmdncGxvdChkYXRhPXF1ZXNkYXRhLmZpbmFsLCBhZXMoeD1HZW5kZXIsIHk9U0FzY29yZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBndWlzZUNvbWJpbmF0aW9uLCBjb2xvcj1ndWlzZUNvbWJpbmF0aW9uLCBsaW5ldHlwZT1HZW5kZXIpLCBhbHBoYT0wLjMsIG5hLnJtPVRSVUUpICsKICBmYWNldF9ncmlkKH5ndWlzZUNvbWJpbmF0aW9uKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1naGlibGlfcGFsZXR0ZSgiUG9ueW9NZWRpdW0iKVtjKDYsMyw0KV0pICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDMsNCldKSArCiAgZ2dfdGhlbWUoKQoKIyBCeSBHdWlzZQpnZ3Bsb3QoZGF0YT1xdWVzZGF0YS5maW5hbCwgYWVzKHg9Z3Vpc2VDb21iaW5hdGlvbiwgeT1TQXNjb3JlKSkgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGd1aXNlQ29tYmluYXRpb24sIGNvbG9yPWd1aXNlQ29tYmluYXRpb24pLCBhbHBoYT0wLjMsIG5hLnJtPVRSVUUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNiwzLDQpXSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1naGlibGlfcGFsZXR0ZSgiUG9ueW9NZWRpdW0iKVtjKDYsMyw0KV0pICsKICBnZ190aGVtZSgpCgpgYGAKCiMjIyMgQ0UgU2NvcmVzCgpgYGB7cn0KIyBEZW5zaXR5IFBsb3Qgb2Ygc2NvcmUgZGlzdHJpYnV0aW9ucwpxdWVzZGF0YS5maW5hbCAlPiUgbXV0YXRlKGd1aXNlQ29tYmluYXRpb24gPSBwbHlyOjptYXB2YWx1ZXMoZ3Vpc2VDb21iaW5hdGlvbiwgZnJvbSA9IGMoImJhc2VsaW5lIiwgIm1hdGNoIiwgIm1pc21hdGNoIiksIHRvID0gYygiTm8tR3Vpc2UiLCAiR3Vpc2UtTWF0Y2giLCAiR3Vpc2UtTWlzbWF0Y2giKSkpICU+JQogIGdncGxvdChhZXMoeD1DRXNjb3JlLGZpbGw9Z3Vpc2VDb21iaW5hdGlvbixjb2xvcj1ndWlzZUNvbWJpbmF0aW9uKSkrCiAgIGdlb21fZGVuc2l0eShhbHBoYT0wLjMpICsKICBsYWJzKHRpdGxlPSIiLCB4PSJDRSBTY29yZSIsIHk9IkNvdW50IikgKwogIGdnX3RoZW1lKCkKCmBgYAoKYGBge3J9CiMgQnkgR2VuZGVyCmdncGxvdChkYXRhPXF1ZXNkYXRhLmZpbmFsLCBhZXMoeD1HZW5kZXIsIHk9Q0VzY29yZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGxpbmV0eXBlID0gR2VuZGVyKSwgZmlsbD0iZ3JleSIsIGFscGhhPTAuMywgbmEucm09VFJVRSkgKwogIGdnX3RoZW1lKCkKCiMgQnkgR2VuZGVyICsgR3Vpc2UKZ2dwbG90KGRhdGE9cXVlc2RhdGEuZmluYWwsIGFlcyh4PUdlbmRlciwgeT1DRXNjb3JlKSkgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGd1aXNlQ29tYmluYXRpb24sIGNvbG9yPWd1aXNlQ29tYmluYXRpb24sIGxpbmV0eXBlPUdlbmRlciksIGFscGhhPTAuMywgbmEucm09VFJVRSkgKwogIGZhY2V0X2dyaWQofmd1aXNlQ29tYmluYXRpb24pICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNiwzLDQpXSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1naGlibGlfcGFsZXR0ZSgiUG9ueW9NZWRpdW0iKVtjKDYsMyw0KV0pICsKICBnZ190aGVtZSgpCgojIEJ5IEd1aXNlCmdncGxvdChkYXRhPXF1ZXNkYXRhLmZpbmFsLCBhZXMoeD1ndWlzZUNvbWJpbmF0aW9uLCB5PUNFc2NvcmUpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gZ3Vpc2VDb21iaW5hdGlvbiwgY29sb3I9Z3Vpc2VDb21iaW5hdGlvbiksIGFscGhhPTAuMywgbmEucm09VFJVRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDMsNCldKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWdoaWJsaV9wYWxldHRlKCJQb255b01lZGl1bSIpW2MoNiwzLDQpXSkgKwogIGdnX3RoZW1lKCkKCmBgYAoKIyMjIyBQQ0EgT3V0cHV0IFNjb3JlcwpgYGB7cn0KCkNTQ0UuYmlwbG90IDwtIHF1ZXNkYXRhLmZpbmFsICU+JSBtdXRhdGUoZ3Vpc2VDb21iaW5hdGlvbiA9IHBseXI6Om1hcHZhbHVlcyhndWlzZUNvbWJpbmF0aW9uLCBmcm9tID0gYygiYmFzZWxpbmUiLCAibWF0Y2giLCAibWlzbWF0Y2giKSwgdG8gPSBjKCJOby1HdWlzZSIsICJHdWlzZS1NYXRjaCIsICJHdWlzZS1NaXNtYXRjaCIpKSkgJT4lCmdncGxvdChhZXMoeT1DRXNjb3JlLCB4PVNBc2NvcmUsIGNvbG9yPWd1aXNlQ29tYmluYXRpb24sIHNoYXBlPWd1aXNlQ29tYmluYXRpb24pKSArIAogIGdlb21fcG9pbnQobmEucm09VFJVRSwgc2l6ZT0zLCBhbHBoYT0wLjcpICsKICAjZ2VvbV90ZXh0KGFlcyhsYWJlbD1zdWJqZWN0KSxoanVzdD0xLjUsdmp1c3Q9MS41LCBjb2xvcj0iIzAwQUZCQiIsIGNoZWNrX292ZXJsYXA9VCkrCgogIHNjYWxlX3hfY29udGludW91cyhsaW09YygtMi41LCAyLjUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW09YygtMy41LCAzLjUpLGJyZWFrcz1zZXEoLTMsMywxKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGxhYnMoeT0iQ0UgU2NvcmVzIChQQzEpIiwgeD0iU0EgU2NvcmVzIChQQzIpIiwgY29sb3I9IlBhcnRpY2lwYW50IEdyb3VwIiwgc2hhcGU9IlBhcnRpY2lwYW50IEdyb3VwIikgKwogIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTWVkaXVtIilbYyg2LDMsNCldKSArCiAgZ2dfdGhlbWUoKQpDU0NFLmJpcGxvdAoKI2dnc2F2ZShwYXRoPSJwbG90cyIsIGZpbGVuYW1lPSJDUy1DRV9kaXN0cmlidXRpb25fcGxvdC5wbmciLCBDU0NFLmJpcGxvdCwgd2lkdGg9MTYsIGhlaWdodD04LCB1bml0cyA9ICJpbiIgLCBkcGk9NzIpCgpgYGAKCiMjIyMgQ29ycmVsYXRpb25zCgpgYGB7cn0KcXVlc2RhdGEuZmluYWwgJT4lIAogIGdncGxvdChhZXMoeCA9IFNBc2NvcmUsIHkgPSBFUXNjb3JlKSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5IiwgYWVzKGNvbG91ciA9IGZhY3RvcihHZW5kZXIpKSwgY2V4PTIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwoKICBsYWJzKHkgPSAiRVEgU2NvcmUiLCB4ID0gIlNBIFNjb3JlIiwgY29sb3I9IkdlbmRlciIsCiAgICAgICB0aXRsZT0iQ29ycmVsYXRpb25zOiBCeSBTcGVha2VyIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTGlnaHQiKVtjKDQsMywyKV0pKwogIGdnX3RoZW1lKCkKYGBgCgpgYGB7cn0KcXVlc2RhdGEuZmluYWwgJT4lIAogIGdncGxvdChhZXMoeCA9IFNBc2NvcmUsIHkgPSBDRXNjb3JlKSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5IiwgYWVzKGNvbG91ciA9IGZhY3RvcihHZW5kZXIpKSwgY2V4PTIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwoKICBsYWJzKHkgPSAiQ0UgU2NvcmUiLCB4ID0gIlNBIFNjb3JlIiwgY29sb3I9IkdlbmRlciIsCiAgICAgICB0aXRsZT0iQ29ycmVsYXRpb25zOiBCeSBTcGVha2VyIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTGlnaHQiKVtjKDQsMywyKV0pKwogIGdnX3RoZW1lKCkKYGBgCgpgYGB7cn0KcXVlc2RhdGEuZmluYWwgJT4lIAogIGdncGxvdChhZXMoeCA9IENFc2NvcmUsIHkgPSBFUXNjb3JlKSkgKyAKICBnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5IiwgYWVzKGNvbG91ciA9IGZhY3RvcihHZW5kZXIpKSwgY2V4PTIpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwoKICBsYWJzKHkgPSAiRVEgU2NvcmUiLCB4ID0gIkNFIFNjb3JlIiwgY29sb3I9IkdlbmRlciIsCiAgICAgICB0aXRsZT0iQ29ycmVsYXRpb25zOiBCeSBTcGVha2VyIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2hpYmxpX3BhbGV0dGUoIlBvbnlvTGlnaHQiKVtjKDQsMywyKV0pKwogIGdnX3RoZW1lKCkKYGBg